To hopefully bypass syntax bikeshed, I'm using an attribute #[replaceable] to mark replaceable impls here. The hope is that #[replaceable] should be able to straightforwardly evolve forwards-compatibly to be expressed in terms of a proc macro attribute on top of today's feature(min_specialization) but be easier to show sound and stabilize sooner.
In short: allow writing #[replaceable] impl<T, ⋯> Trait for T { ⋯ } where the blanket impl is "replaceable" by a specific implementation.
I will use the following definitions from RFC#2451:
- Local Trait
A trait which was defined in the current crate. Whether a trait is local or not has nothing to do with type parameters. Given
trait Foo<T, U>,Foois always local, regardless of the types used forTorU.- Local Type
A struct, enum, or union which was defined in the current crate. This is not affected by type parameters.
struct Foois considered local, butVec<Foo>is not.LocalType<ForeignType>is local. Type aliases and trait aliases do not affect locality.- Covered Type
A type which appears as a parameter to another type. For example,
Tis uncovered, but theTinVec<T>is covered. This is only relevant for type parameters.
In some more detail:
#[replaceable] impl TraitTraitmust be a local traitSelfmust be an uncovered genericTraitmust only contain functions, and these functions cannot have defaulted bodies[1]- Must not overlap another
#[replaceable] implfor the sameTrait
- Other
impl Trait- May overlap a
#[replaceable] implbut only if for a local type- Conflicts with
#[replaceable] implotherwise (for uncovered nonlocal types)
- Conflicts with
- Must be complete as if the replaceable implementation did not exist
- May overlap a
- Trait lookup
- When asking if a type implements
Trait, we can now get two providing impls: take the one not marked#[replaceable] - Auto(de)ref lookup order happens normally, so a replaceable impl can still be chosen before a normal impl if it is for an earlier type in the lookup chain
- When asking if a type implements
If I understand default correctly, this should behave just as a highly restricted form of making an implementation where all members are marked default.
The point is that this is a even more minimal subset of specialization which is even more obviously sound and easier to understand, mainly as the "always applicable" rule is replaced with the simpler "local type" rule. In a similar vein, the restriction of default method bodies in the trait is so that there is no question about which function gets used if a replacement implementation relies on the default implementation (since it could potentially come from the one in the trait or the #[replaceable] impl).
I have two specific questions for the irlo crowd:
- Is this a proper forwards-compatible subset of
min_specialization, such that#[replaceable]could be replaced with a simple token transformation usingdefaultin the future? - Is this a meaningful enough subset of
min_specializationthat a) it could be stabilized sooner and b) it's worth stabilizing sooner?
I think it's possible to incrementally relax #[replaceable] to the always applicable rule, allow partial replacement, etc all the way to default, but I'm not certain.
This restriction exists solely to make
#[replaceable]relaxable to a procedural macro attribute which only sees theTokenStream, as the attribute cannot mark functions not present in theimplit sees asdefault. Additionally, this makes it so that the replacement implementation also specifies every function, so there is no question ↩︎