I'm attempting to implement TryFrom<AsRef<str>> for my enum to convert from String or &str, or anything that is AsRef<str> really. I think the solution is probably to use std::str::FromStr instead, but I'd like to understand why my approach doesn't work, and how I might self-service with the error given to connect all the pieces.
The following code does not compile:
enum Num {
One,
Two,
}
impl<T> TryFrom<T> for Num
where
T: AsRef<str>,
{
type Error = &'static str;
fn try_from(as_ref: T) -> Result<Num, Self::Error> {
match as_ref.as_ref() {
"1" => Ok(Num::One),
"2" => Ok(Num::Two),
_ => Err("unknown number"),
}
}
}
fn main() {
let one: Num = "1".try_into().unwrap();
let s = "2".to_string();
let two: Num = s.try_into().unwrap();
}
And instead produces this error:
Compiling playground v0.0.1 (/playground)
error[E0119]: conflicting implementations of trait `std::convert::TryFrom<_>` for type `Num`
--> src/main.rs:6:1
|
6 | impl<T> TryFrom<T> for Num
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T, U> TryFrom<U> for T
where U: Into<T>;
For more information about this error, try `rustc --explain E0119`.
error: could not compile `playground` due to previous error
The way I've interpreted the error is that somehow there's an implementation of Into for Enum when there isn't. I feel like there's one small piece I'm missing to finish this puzzle and I just can't find it. Any insight is much appreciated.
It's not that there necessarily is such an impl currently, but there may be one added in the future by some code that you don't control. The compiler has to conservatively protect against this possibility, or else the addition of such an impl could silently and unexpectedly break your code.
impl<T, U> TryFrom<U> for T where U: Into<T>
// Covers
impl<U> TryFrom<U> for Num where U: Into<Num>
But if I'm downstream from you, I'm allowed to implement both of, for example,
impl From<MyStruct<Num>> for Num { /* ... */ }
// The above also results in `Into<Num> for MyStruct<Num>`
impl<T> AsRef<str> for MyStruct<T> {}
So that's the potential conflict under today's rules.
It comes up with TryFrom, TryInto, From, and Into a lot because of their blanket implementations. The upshot is you need a helper conversion struct (not ergonomic) or you implement for specific types, not generically. Or you need specialization.
I hadn't considered that this was more of a limitation/protection from the compiler's perspective but it makes perfect sense the way you've laid it out. Thanks for the detailed response!