- Feature Name: impl_else
- Start Date: 5/16/2016
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
Allow multiple conflicting impl
s 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 impl
s. 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 impl
s. 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" impl
s 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 impl
s.
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 impl
s 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 impl
s 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 impl
s as nodes and conflicts as edges, the graph
should be connected; no node should be unreachable. This is to discourage placing
unrelated impl
s 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?