Transitive or recursive .into()

I cannot do the first line, even though all necessary steps are possible. There is no way to combine this, without switching to the inner type's converters (like to_string() in this case.) That loses the elegance of into(). Or I have to take it apart, like the last two lines:

let no: Option<String> = "oho".into();
let long: Option<String> = "oho".to_string().into();

let s: String = "aha".into();
let o: Option<String> = s.into();

It would be cool if this could happen recursively!

Unfortunately, recursive Into/From would cause a huge amount of conflict, and stop people from writing direct impls which in many cases will be preferable.

However, in theory we could add an impl From<&str> for Option<String>.

6 Likes

For specifically this case, it's theoretically possible that std could write a blanket impl<T, U> From<T> for Option<U> where T: Into<U>. That impl is currently diagnosed illegal as conflicting with the blanket impl<T> From<T> for T, but so does the existing impl<T> From<T> for Option<T>. The conflict doesn't happen because rustc knows Option<Self> where Self = Option<Self> is an impossible type. This exception doesn't work for the generalized impl because TU, but coherence could determine (or be special cased) that T = U in the only potential overlap case and thus allow the overlap.

Whether we want to is a different question, as is whether adding an impl would be breaking; unfortunately I think it would be for the same reason new blanket impls generally are.

3 Likes

It's not really about the necessary steps being possible bur rather being unique. What if someone declared a type Bad with implemented From<&str> and Into<Option<String>>? Then it would also allow to go from &str to Option<String>, except it may result in a different result than using String as the intermediate step. This means the conversion will always be ambigious without a way to prefer using String (which is essentially what you're currently doing).

3 Likes

Oh, dang – again the problem that concrete implementations don't take precedence over blanket implementations. :face_with_peeking_eye:

None of the implementations I mentioned are blanket though, they are all concrete. The problem in fact are not blanket implementations, but possible diamonds (or even cycles) present in the Into/From graph, which create ambiguity over which path should be taken.

8 Likes