Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

TryFrom / TryInto API Docs and Infallible #97866

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants