- Feature Name: impl_else
- Start Date: 5/16/2016
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
Allow multiple conflicting impls via the else keyword.
impl<T: ?Sized> AttemptDeref for T
where T: Deref {
type Output = <T as Deref>::Target;
fn attempt_deref(&self) -> Self::Output {
*self
}
} else<T: ?Sized> AttemptDeref for T {
type Output = T;
fn attempt_deref(&self) -> Self::Output {
self
}
}
As it stands, it is not possible to create conflicting impls. While RFC 1210
makes targetting more efficient implementations of a trait easy, it has a few
shortcomings.
The first is that default associated items are always subject to being
overridden by future impls. This means that they cannot be resolved to a
specific type/constant when compiling a crate.
The second issue is that specializations require a direct hierarchy of
specificity. default functions and associated items must be for less specific
impl than the "specialized" impls that override them.
pub trait Trait1 {}
pub trait Trait2 {}
pub trait Trait3 {
fn test(self);
}
impl<T: Trait1> Trait3 for T {
default fn test(self) { println!("Trait1") }
}
impl<T: Trait2> Trait3 for T {
fn test(self) { println!("Trait2") }
}
As there is no relationship between Trait1 and Trait2, a type which implements
both would have no clear priority which Trait3 impl to choose.
Adding else clauses would create an ordered priority between conflicting impls.
Associated items would also be well defined and not subject to being overridden.
impl<T: Trait1> Trait3 for T {
fn test(self) { println!("Trait1") }
} else <T: Trait2> Trait3 for T {
fn test(self) { println!("Trait2") }
}
A final benefit is that type_traits similar to those found in C++'s can be easily
created.
trait RemoveReference {
type Type: ?Sized;
}
impl<'a, T: ?Sized> RemoveReference for &'a mut T {
type Output = T;
} else<'a, T: ?Sized> for &'a T {
type Output = T;
} else<T: ?Sized> RemoveReference for T {
type Output = T;
}
This approach is more limited than C++'s SFINAE, but it is still quite powerful
A single impl else block would essentially act as multiple impls but with the
exception that conflicting implementations between the different impls would be
resolved to the first impl in the block that matched.
Within an impl else block, all impls must be involved in internal conflicts,
but cannot be involved in conflicts with other impl or impl else blocks. When
represented as a graph with impls as nodes and conflicts as edges, the graph
should be connected; no node should be unreachable. This is to discourage placing
unrelated impls into impl else blocks.
This approach should integrate cleanly with the specialization of RFC 1210.
default methods and associated items in an impl else block should function
just as if they're in an independent impl.
None at the moment.
More explicit syntax impl... else impl.... Not sure this is necessary as unlike
if statements, there is no concept of a trailing else.
Some kind of where T: !Trait proposal.
Will this also work for non-trait impl blocks?