pre-RFC: default fn impl in std::convert::From


#1

Hello, since Rust 1.4 (the release I got into Rust), I’m struggling with cases where I need to define generic From conversions for any T: MyTrait, like here:

trait Trait:  {}

impl Trait for u32 {}
impl Trait for u64 {}

struct MyType<T: Trait> {
    pub t: T,
}

impl<T: Trait, U: Trait + From<T>> From<MyType<T>> for MyType<U> {
    fn from(rhs: MyType<T>) -> Self {
        MyType { t: U::from(rhs.t) }
    }
}

https://play.rust-lang.org/?gist=2deed0da944c5ea2d1e4a60c54ddf75b&version=nightly&mode=debug

If I understand what’s specialization in Rust is going to be, this problem can be solved through it by changing the From<T> for T impl (and the respective Into) in stdlib with use of a “default” impl.

// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> From<T> for T {
    fn from(t: T) -> T { t }
}

=>

// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> From<T> for T {
    default fn from(t: T) -> T { t }
}

Since specialization is currently nightly, we can feature-gate it inside stdlib until it lands to stable.

I’m writing this post to open a pre-RFC discussion and figure out possible pitfalls in my vision and proposal before writing an RFC. Please be welcome to correct me if I’m wrong!

UPD: checked on the current nightly and it doesn’t seem to work, though I don’t really understand why. Can someone more familiar with specialization feature status explain how this should be implemented and if that’s possible at all? https://play.rust-lang.org/?gist=e0d1db8fbcbd7c646fc8731f0ab99a39&version=nightly&mode=debug


#2

I’m not sure what you think the “Reflexive” case has to do with the original problem which is not a reflexive relationship. T -> T is reflexive. T -> U is not the reflexive case.


#3

@gbutler I don’t understand what you mean by this. The claim is that From is reflexive, which is true.

To me the problem is that U might be T. Perhaps we could introduce syntax for type parameters depending on each other? Not sure if this is generally useful.

... where U: T // U is the same concrete type as T
... where U: !T // U is never the same concrete type as T

#4

The stdlib default implementation provided just says that any Trait T has a trivial reflexive implementation for T -> T. So, that just implements that trivial reflexive case for all T. T->T is always trivially implemented as given in the blanket implementation. I’m not sure I understand how adding “default” to that blanket implementation for the reflexive case has anything to do with the problem the OP stated. It doesn’t seem to be wanting to do T -> T (in the generic case). Yes, T could be U, but, it doesn’t say that it will be.

EDIT: Never mind. I think I understand what the goal is now.


#5

I don’t think your solution would work. If a T -> U conversion would specialize the T -> T one, then so would U -> T, and calling the T -> T function would then require disambiguating between the two using some mechanism that doesn’t currently exist. So you’d probably have to come up with a proper solution to this “diamond specialization” problem rather than just adding default to the blanket impl.

Furthermore, this seems conceptually backwards to me. I’d think the T -> T conversion would be a specialization of T -> U, not the other way around.


#6

Okay, I kinda see the problem here, as I created some confusion calling this conversion T -> U while basically compiler sees both of these conversions as T -> T. In the other case, I can’t find a reason how it comes that T<A> -> T<B> conversion would conflict with generic T -> T conversion implemented in stdlib.

So the question is about specializing the T -> T conversion over the (described in the post) MyType generic arguments.

The question is: is that currently possible or that would require some deep changes in the type system?


#7

Specialization and the trait system implementation are very much in flux these days. Supporting things like impl<T, U> From<Option<T>> for Option<U> where U: From<T> is very much desired – it was mentioned in the extending-?-to-more-types, for example – but whatever that will be probably can’t be determined until those prerequisites are complete, and right now that’s lower priority than the 2018 edition.