impl<T, U> From<Option<T>> for Option<U> where T: Into<U>


#1

I wonder: Will this ever work?

let x: Option<&'static str> = Some("foo");
let y: Option<String> = x.into();
// error[E0277]: the trait `std::convert::From<std::option::Option<&str>>`
//       is not implemented for `std::option::Option<std::string::String>`

I am not very familiar with specialization rules, but I intuitively expected impls with one type parameter to be more specialized than impls with two type parameters. But that does not seem to be the case. Otherwise std could implement impl<T, U> From<Option<T>> for Option<U> where T: Into<U>, see this example:

https://play.rust-lang.org/?gist=fe64d00eee81bba35ab62b12e0ea81d0&version=stable&backtrace=0

enum MayBe<T> { // corresponds to ::std::option::Option
    Yup(T),
    Nope,
}

use MayBe::{Yup, Nope};

// T -> U  =>  T -> Option<U>
impl<T, U> From<MayBe<T>> for MayBe<U> where T: Into<U> {
    fn from(x: T) -> Self {
        x.map(|value| value.into())
    }
}

// For `T = U` this seems to conflict with `impl<T> From<T> for T`
//
// error[E0119]: conflicting implementations of trait
//      `std::convert::From<MayBe<_>>` for type `MayBe<_>`

Is there a perspective to improve specialization further, or will something like this never happen?

cc @killercup


#2

I was about to ask a similar trait conflict question so I’ll piggy back onto this one. I have no idea why the impls are conflicting in this program. Thoughts anyone?

https://play.rust-lang.org/?gist=9bf0c30523c8e39c553d7294f1bbf37c&version=nightly&backtrace=0


#3

This seems like a wrong intuition to me. Result<Vec<T>, E> is not clearly less specialized than than Result<T, T> in my opinion.

In fact, we were considering the opposite ordering - because Option<T> and Option<U> are less abstract than T (they’re constrained to be some sort of option), that impl is more specialized than the generic one.

But this is a future extension to specialization which hasn’t yet been implemented.


#4

My intuitive reasoning is mostly based on a geometric analogy. I imagine an impl for a concrete type to be point-like, an impl with one type parameter would be a parametric one dimensional line, etc.

Afaik the current system treats point-like impls as more specific than any one-dimensional impl. Is this correct, or are there already exceptions?

If there are no exceptions for 0 vs 1 dimension, then my intuition still is to generalize this to n vs. n+1 dimensional parametric manifolds even if the higher dimensional manifold has more ‘structured’.

Could you perhaps point me to a discussion of this topic? I wonder if there are benefits to have the reverse ordering.


PS: Do you think impl<T, U> From<Option<T>> for Option<U> where T: Into<U> or even ... From<T> ... might become a thing someday (with whatever ordering)? The use case would be generic setter functions for say a field foo: Option<String> - then foo("hey"), foo(None), foo(Some("hey")) would all work.


#5

I have to admit your geometric analogy is lost on me; I just don’t have the mathematical background to understand it.

The idea we have is to make the impl you want added more specific than the impl that exists, you can read about the justification for that here: http://smallcultfollowing.com/babysteps/blog/2016/10/24/supporting-blanket-impls-in-specialization/


#6

That doesn’t hold in general. To borrow your geometric analogy, what you have here is a line <T, T> and a plane <Maybe<T>, Maybe<U>> in which neither is wholly contained within the other (line ⊈ plane), so neither is more specialized than the other. For example, <(), ()> is contained within the line but not within the plane, yet <Maybe<()>, Maybe<i64>> is contained within the plane but not within the line.


#7

Thx. I was not aware, that one impl must be a strict subset of the other. I assumed it was based on a more broad notion of one impl being more specific than the other. I also assumed that needing less type parameters is more specific in a sense that a line has less volume than a plane (…very handwavingly).

With this strict rule that one impl must be a subset, these kinds of partially overlapping impls will never happen, unless Rust invents some negative where clause where T != U, right?


#8

There was also a proposal for intersection impls, where you’d add a third impl for <Maybe<T>, Maybe<T>> which would leave the remains of the other two independent.