Reserving Trait Impl's In Rust

I often find myself wanting to call a function like my_usize.try_into::<u16>(). This function (and similar) is defined in unstable rust through the TryFrom and TryInto traits. Unfortunately, stabilization of these traits is currently blocked by stabilization of the ! type, because we (the royal we) want to provide default implementations of TryFrom/TryInto that return Result<T, !> for any types that already implement From/Into. If we stabilized TryFrom/TryInto before stabilizing the ! type, then we would have to wait and add the blanket implementation later, once ! is stable. But since trait implementation specialization is not (currently) allowed, adding the blanket implementation later would be a breaking change; any crates that implemented both Into<Foo> for Bar and TryInto<Foo> for Bar, for example, would no longer compile.

This is a bit of a problem. TryFrom/TryInto has been implemented in unstable since May 4, 2016. That’s more than 2 years ago. And its only blocker is our inability to reserve the desired blanket impl’s for later use. This strongly suggests, to me, that we need a mechanism for reserving trait impl’s. In the tracking issue for the TryFrom and TryInto traits, a couple of solutions were suggested: a) add an always-on error lint to prevent implementing both Into<Foo> for Bar and TryInto<Foo> for Bar, or b) add an unstable blanket impl impl<T, U> TryFrom<U> for T where T: From<U>. The former might be possible with the current lint system (I don’t know), but the latter is not currently possible, because trait impl’s cannot be marked as unstable. Nonetheless, it’s important to note that neither of these approaches are available to crate authors. What if a crate author wants to reserve a blanket trait impl for future use? There’s no way to do that other than to keep the crate unstable and warn users of the pending breaking change.

I would like to suggest that we provide a more general mechanism for reserving trait impl’s. Here are a few syntactical approaches we could take:

1) reserve keyword

reserve impl<T, U> TryFrom<U> for T where T: From<U>;

or

reserve<T, U> TryFrom<U> for T where T: From<U>;

2) #[reserve] attribute

#[reserve]
impl<T, U> TryFrom<U> for T where T: From<U>;

3) Implicit reservation

impl<T, U> TryFrom<U> for T where T: From<U>;

Regardless of the syntax, though, the semantics would be the same: the impl would be reserved for future definition; conflicting impl’s would produce compiler errors. This would make it possible to stabilize the TryFrom and TryInto traits now, with the blanket impl’s for From and Into reserved for later definition. This would also make it possible for crate authors to reserve blanket impl’s for traits that they define.

As far as how difficult this would be to implemented in the compiler, I cannot comment, but I’m hoping someone from the compiler team can.

Comments

If you have any comments or questions, please join the Reddit discussion!

Written on September 18, 2019