Using try_into when implementing TryFrom

Hi,

when reading the documentation for TryFrom here it is mentioned that TryFrom<T> for U implies TryInto<U> for T.

This simple example

enum Code {
    Hello,
    Bye,
}

impl TryFrom<u8> for Code {
    type Error = String;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::Hello),
            1 => Ok(Self::Bye),
            _ => Err(String::from("ERROR")),
        }
    }
}

fn main() {
    let code = Code::Bye;
    
    let code_as_u8: u8 = match code.try_into() {
        Ok(c) => c,
        Err(err) => {
            println!("{}", err);
            return;
        },
    };
}

fails to compile with the following error:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `u8: From<Code>` is not satisfied
  --> src/main.rs:20:32
   |
20 |     let code_as_u8: u8 = match code.try_into() {
   |                                ^^^^ -------- required by a bound introduced by this call
   |                                |
   |                                the trait `From<Code>` is not implemented for `u8`
   |
   = help: the following other types implement trait `From<T>`:
             <u8 as From<NonZeroU8>>
             <u8 as From<bool>>
   = note: required for `Code` to implement `Into<u8>`
   = note: required for `u8` to implement `TryFrom<Code>`
   = note: required for `Code` to implement `TryInto<u8>`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

Am I missing something here? When I implement TryInto manually (the docs state that it is not recommended), the code compiles without any issues.

Thanks in advance for the help!

You are mixing up your types. TryFrom<u8> for Code implies TryInto<Code> for u8. That means you can call 0_u8.try_into() to get an instance of Code, not convert a Code into u8. If you want to convert Code to u8 I'd recommend implementing From<Code> for u8. Hope this helps:

#[derive(Debug, PartialEq)]
enum Code {
    Hello,
    Bye,
}

impl TryFrom<u8> for Code {
    type Error = String;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::Hello),
            1 => Ok(Self::Bye),
            _ => Err(String::from("ERROR")),
        }
    }
}

impl From<Code> for u8 {
    fn from(c: Code) -> Self {
        match c {
            Code::Hello => 0,
            Code::Bye => 1,
        }
    }
}

fn main() {
    let c: Code = 0_u8.try_into().unwrap();
    
    assert_eq!(c, Code::Hello);
    
    assert_eq!(u8::from(c), 0);
    
    let b: u8 = Code::Bye.into();
    
    assert_eq!(b, 1);
}

Playground.

1 Like

You have the answer here, the type variables just make it hard to read. Replace them with the specific types in your scenario!

T = u8, U = Code
TryFrom<u8> for Code implies TryInto<Code> for u8.

Oh wow, that's super obvious. I blanked out super hard on this... Thanks for pointing it out @jofas and @semicoleon!

I will implement From<Code> for u8 just like @jofas suggested. That's exactly what I need.

2 Likes

If you think about it, this couldn't really be the other way around. If TryFrom<T> for U implied TryInto<T> for U (with a useful/correct behavior, that is), it would mean that the compiler could invert arbitrary functions! I would love myself such a compiler, but it's not going to happen.

Not only it is impossible in the general case (it's right there with the halting problem in terms of difficulty, not to mention functions which are simply non-invertible), it would also severely contradict the locally encapsulated design of the language. The compiler would need to look at the implementation, not just the interface, of one trait in order to generate another.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.