When impl Display and impl TryFrom<&str>, I can't type coerce

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?

Found this explanation from @SkiFire13 : implicit Deref coercion not happening · Issue #109829 · rust-lang/rust · GitHub.

3 Likes

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:

trait MyFrom<T>: Sized {
    fn my_from(s: T) -> Result<Self, ()>;
}

impl MyFrom<&str> for A {
    fn my_from(_s: &str) -> Result<Self, ()> {
        Ok(A::Y)
    }
}

Then it will deref no problem.

A::my_from(&s) // Ok(A::Y)

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)

A bonus to implementing FromStr is that str.parse() comes along for the ride, even when no parsing is involved!

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.