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.
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);
}
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.