So I ran into an odd problem regarding Display, TryFrom, and using .to_string().
When I impl Display, I should get an impl ToString, ref, and maybe I'm getting that, I can't tell from compiler explorer since I don't see a ToString in that output.
But I wrote this scenario in the playground, and I'm wondering why the coercion from &String to &str isn't working. What am I missing?
Yeah, this seems to be because impl<T, U: Into<T>> TryFrom<U> for T exists, so it only checks if &String implements Into<A>, and isn't allowed to check &str. If this blanket impl didn't exist, it seems like it would be fine dereffing to TryFrom<&str> when TryFrom<&String> isn't found.
This introduces a sneaky way to cause breaking changes. If you have a trait like this:
But if you add an impl for &String, it will change the behavior of the previous line:
impl MyFrom<&String> for A {
fn my_from(_s: &String) -> Result<Self, ()> {
Err(())
}
}
A::my_from(&s) // Err(())
Obviously, it's a bad idea for various reasons to make impls of a type and its deref functionally different.
And just so there's no loose ends, when you don't need the lifetime of the &str, you can use FromStr instead of TryFrom<&str>. It's fine to implement both. And if you actually need to call try_from, then you can convert it manually or use the specific trait:
A::try_from(&*s)
A::try_from(s.as_str())
A::try_from(s.as_ref())
A::try_from(s.deref())
A::try_from(&s[..])
TryFrom::<&str>::try_from(&s)
<A as TryFrom<&str>>::try_from(&s)