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>
,Foo
is always local, regardless of the types used forT
orU
.- Local Type
A struct, enum, or union which was defined in the current crate. This is not affected by type parameters.
struct Foo
is 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,
T
is uncovered, but theT
inVec<T>
is covered. This is only relevant for type parameters.
In some more detail:
#[replaceable] impl Trait
Trait
must be a local traitSelf
must be an uncovered genericTrait
must only contain functions, and these functions cannot have defaulted bodies[1]- Must not overlap another
#[replaceable] impl
for the sameTrait
- Other
impl Trait
- May overlap a
#[replaceable] impl
but only if for a local type- Conflicts with
#[replaceable] impl
otherwise (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 usingdefault
in the future? - Is this a meaningful enough subset of
min_specialization
that 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 theimpl
it sees asdefault
. Additionally, this makes it so that the replacement implementation also specifies every function, so there is no question âŠī¸