Skip to content

TryFrom / TryInto API Docs and Infallible #97866

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
amunra opened this issue Jun 8, 2022 · 6 comments
Closed

TryFrom / TryInto API Docs and Infallible #97866

amunra opened this issue Jun 8, 2022 · 6 comments

Comments

@amunra
Copy link

amunra commented Jun 8, 2022

I had a hard time figuring out how design a function signature to pass an object to a method that can take either a Thing or a TryInto<Thing, Error=MyError>.

I think there should be an example on how to do this somewhere in the API docs, probably under TryInto.

Here's what it took me to get this working:

use std::convert::{TryFrom, TryInto, Infallible};

#[derive(Debug)]
pub struct ModError(String);

pub struct Name<'a> {string: &'a str}

impl <'a> Name<'a> {
    pub fn new(string: &'a str) -> Result<Self, ModError> {
        match string.starts_with('A') {
            true => Ok(Self{string: string}),
            false => Err(ModError(format!("Bad name {:?}", string)))
        }
    }
    
    pub fn string(&self) -> &str { self.string }
}

impl <'a> TryFrom<&'a str> for Name<'a> {
    type Error = ModError;

    fn try_from(name: &'a str) -> Result<Self, ModError> {
        Name::new(name)
    }
}

impl From<Infallible> for ModError {
    fn from(_: Infallible) -> Self {
        unreachable!()
    }
}

pub fn greet<'a, N, E>(name: N) -> Result<(), ModError>
    where
        N: TryInto<Name<'a>, Error=E>,
        ModError: From<E>
{
    let name: Name<'a> = name.try_into()?;
    println!("Hello, {}!", name.string());
    Ok(())
}

fn main() {
    let alfred = "Alfred";
    let name = Name::new(alfred).unwrap();
    greet(name).unwrap();
}
@ChayimFriedman2
Copy link
Contributor

I don't think this usage is common enough that we need to put an example on the docs. Most of the times you're generic over the error type, or need a concrete error type, but not either of error types.

@amunra
Copy link
Author

amunra commented Jun 9, 2022

I'm not intentionally trying to be generic over multiple error types here, but rather just I'm trying to get a function to take either T or something that can try into Result<T, MyError>.

Supplying T directly works out of the box with From/Into and is pretty surprising it doesn't with TryFrom/TryInto: I think it's a pretty major gotcha.

As far as I understand, these types are provided to allow designing flexible APIs that "overload" over compatible types. I don't see how that usage is uncommon: It's likely I'm misunderstanding something.

Side note: Will stabilizing the ! never type in nightly make this work out of the box? There's mention of this problem in those docs: https://doc.rust-lang.org/std/primitive.never.html#infallible-errors

@eggyal
Copy link
Contributor

eggyal commented Jun 9, 2022

You can do this instead:

pub fn greet<'a, N>(name: N) -> Result<(), ModError>
    where
        N: TryInto<Name<'a>>,
        ModError: From<N::Error>,
{
    let name: Name<'a> = name.try_into()?;
    println!("Hello, {}!", name.string());
    Ok(())
}

A minor improvement, perhaps, but at least you don't need the explicit E type parameter.

Will stabilizing the ! never type in nightly make this work out of the box?

You'll still need the ModError: From<N::Error> type constraint, but you won't need to provide an impl From<!> for ModError since there's a blanket impl<T> From<!> for T.

@ChayimFriedman2
Copy link
Contributor

but you won't need to provide an impl From<!> for ModError since there's a blanket impl<T> From<!> for T.

You'll still need it because it's only a reserved impl (unless it'll be a blocker for stabilization).

@eggyal
Copy link
Contributor

eggyal commented Jun 9, 2022

You'll still need it because it's only a reserved impl (unless it'll be a blocker for stabilization).

Ah, so it is. For anyone interested see #57012 (comment) and #64631 (comment).

@jyn514
Copy link
Member

jyn514 commented Apr 15, 2023

I'm going to close this as not a bug; we can't document every possible use case and I agree with @ChayimFriedman2 that this one isn't common enough to be in the official docs.

@jyn514 jyn514 closed this as not planned Won't fix, can't repro, duplicate, stale Apr 15, 2023
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants