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
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
TryInto that return
Result<T, !> for any types that already implement
Into. If we stabilized
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.
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
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:
reserve impl<T, U> TryFrom<U> for T where T: From<U>;
reserve<T, U> TryFrom<U> for T where T: From<U>;
#[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
TryInto traits now, with the blanket impl’s for
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.
If you have any comments or questions, please join the Reddit discussion!