Motivation
Currently, it’s impossible to restrict a default method body to a set of bounds more specific to those of the trait. (Prior discussion: Conditional default trait method impls). In addition, there are cases where, for correctness, implementing one trait places restrictions on implementations of other traits, but enforcing those restrictions is difficult at present. Finally, it is currently impossible to define trait methods with default bodies that are impossible to override.
Explanation
We introduce a new type of trait impl
: partial impl
. The syntax is exactly the same as a normal trait impl
, except that:
- The contextual keyword
partial
is added before the keywordtrait
, optionally preceded withdefault
, and - Not all the trait’s items need to be included, any or all may be omitted.
A partial impl Trait for Type
does not actually cause Trait
to be implemented; a non-partial
impl
is necessary for this. Such an impl
is necessary to access any of the items defined in the partial impl
block. But that non-partial impl
can omit any items that were already defined by the applicable partial impl
. In addition, if the partial impl
was not annotated with default
, then the non-partial impl
is not permitted to override its items.
partial impl
s must obey all the same orphan rules as non-partial
impl
s. In addition, non-default
partial impl
s are not permitted to overlap if they both define the same item. default partial impl
overlaps are permitted, however; the rules for them are defined later in this document.
Examples
//! Providing defaults that are not always applicable.
//! Example from https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412
fn require_sized<T: Sized>(_: &T) {}
trait Foo {
fn foo(&self);
}
default partial impl<T: Sized> Foo for T {
fn foo(&self) {
require_sized(self);
}
}
struct Bar;
impl Foo for Bar {
// no need to provide `foo()`, it’s taken from the `partial impl`.
// But I could override it if I wanted, as the `partial impl` is marked `default`.
}
fn main() {
let x: &dyn Foo = &Bar;
x.foo();
}
//! Enforcing relationships between traits.
trait Foo {
fn frob(&self);
fn bork(&self);
}
/// Implementors of `BorkFrobsTwice` implement `Foo` such that
/// `Foo::bork()` calls `Foo::frob()` exactly twice. This can be relied upon for soundness.
trait BorkFrobsTwice: Foo {}
partial impl Foo for BorkFrobsTwice {
fn bork(&self) {
self.frob();
self.frob();
}
}
//! Non-overridable auxiliary trait methods.
trait Foo {
fn frobnicate(&self);
/// Calls `frobnicate` twice. Does nothing else.
/// You can rely on that for soundness.
fn frobnicate_twice(&self);
}
partial impl<T: ?Sized> Foo for T {
fn frobnicate_twice(&self) {
self.frobnicate();
self.frobnicate();
}
}
Precedence of default
s
When a non-partial
trait impl
does not define one of the trait’s items, and the item is not provided in an applicable non-default
partial impl
either, we try to take the definition from an applicable default partial impl
, or from the default implementation provided in the trait definition. However, there may be multiple defaults that could apply, so we choose among them with the following rules:
When looking for a default implementation of an item to complete an impl<...> Trait<...> for Type<...>
block (where ...
represents 0 or more generics):
- First, look for implementations in applicable
default partial impl<...> Trait<...> for Type<...>
blocks.- “Applicable” means that the
where
clauses can’t be more restrictive than those of theimpl
block being completed. - If there are several, choose the one with the most restrictive bounds.
- This includes bounds on
Type
, as well as bounds on the generic parameters ofTrait
.
- This includes bounds on
- If that is not enough to disambiguate, choose the one in the most “downstream” crate—the one that depends on the other.
- If that is still not enough to disambiguate, then no default applies, and the
impl
being completed must provide the item itself.
- “Applicable” means that the
- If none was found, look for implementations in applicable
default partial impl<T: Bound> Trait for T
blocks.- If there are several, choose the one with the most restrictive bounds.
- If that is still not enough to disambiguate, then no default applies, and the
impl
being completed must provide the item itself.
- If none was found, look for a default in the trait definition.
- If there is none, then the
impl
being completed must provide the item itself.
- If there is none, then the