Question about specialization of complex types


#1

Hi,

I was trying the following today (with nightly, specialisation turned on):

impl<Y: From<X>, X> From<Option<X>> for Option<Y> {
    fn from(self: Option<X>) {
        match self {
            Some(x) => Some(x.from()),
            None => None
       }
   }
}

This fails, because there is already a blanket implementation of From<T> for T. I was assuming specialization would solve such cases, but it seems not.

Intuitively, I would say that Option<T> is more specialized then T, but this doesn’t seem to be the case. Curiously though, I have found no mention of these cases in the relevant RFC, nor can I come up with a reason why this would be a problem. I could be wrong though, just because I haven’t found a problematic example doesn’t mean there isn’t one :).

Can someone shed some light in here for me?

(playpen)


#2

Did you implement this within std::option? If not, I’d guess you run afoul of the orphan rule.

Also I doubt you can create a useful implementation of From<Option<T>> for U unless you either panic on None or require U : Default.


#3

I agree that Option<T> is should be more specialized than T, and though the RFC is rather vague (referring only to substituting a type variable with a fully concrete type, rather than with a parameterized type), it is the case today.

The first problem you are running into is that the from method in the From<T> for T impl is not specializable (syntactically, it does not have default before the fn). You cannot specialize that impl.

I also don’t think this impl is actually specialized from the blanket impl - it is only a subset if Y=X, otherwise it is not. Therefore this impl would not be valid even if the blanket impl were specializable.

Lastly you cannot define this impl anywhere except libcore because of the orphan rules.


#4

If you both have a look at the linked playpen, I just quickly created my own Option type to get around that for illustration purposes :). (Same for From)

Current implementation of std::Option, From and the Orphan rule aside, the problem is conflicting implementations.

The question here is not to implement for U but for Option<U>, which cannot panic if T -> U doesn’t panic. (Basically: If From is defined for the type, there’s also a From for the wrapped type)

Ah, I think I understand. So the problem is that the implementation for Option<X> for Option<Y> with X!=Y doesn’t cover From<T> for T while it would it X=Y.


#5

Yeah. To write this we would need the lattice form of specialization and then an additional impl<T> Option<T> for Option<T> (to cover the ‘intersection’ between these two overlapping impls).


#6

Right.

If I see that right from the RFC, the lattice form of specialization is “just” an extension of the current form and might happen in the future. This seems like a rather usual case for me.

If I read the tracking issue right, this is still an open question. I’ll move my comments over there.

Thanks for the clarifications! :slight_smile: