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

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 Likes

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.

@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

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.

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.

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?

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.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.