Warning: This is a half-baked idea.
There are many soundness issues with specialization in its current form. I would like to propose an alternative. While it may not be as powerful, it should be sound due to its simplicity.
where clause statement
Typically, specialization is used to implement special behavior for types implementing a specific trait. Instead of replacing the impl with a new one (requiring trait selection to be vigilant wrt overlapping impls), I propose a statement with similar semantics as C++ if constexpr, but exclusively for trait bounds:
pub trait CloneInto: Clone {
// An implementation on Vec can reuse the existing allocation.
fn clone_into(&self, dst: &mut Self);
}
pub fn clone_into<T: Clone>(value: &T, dst: &mut T) {
where T: CloneInto {
// Within this block, T also implements CloneInto.
value.clone_into(dst)
} else {
// Otherwise, fall back to Clone.
*dst = value.clone();
}
}
To summarize:
- At compile time, if the specified bounds are true the given block is used. Others are discarded.
- The types implement traits from the where bounds within the block.
- All bounds are supported here.
- If possible, simple type equality bounds could be supported.
- Like if statements, where clause statements also have else and else where statements.
One downside (upside) is that this does not modify trait selection. While this is strictly less powerful than trait-level specialization, it meets most of the demand and doesn't touch the trait solver (which seems to be a common way to cause unsoundness).
More concretely, this still does not allow blanket trait impls to be overridden (which is why clone_into is a free fn). Specialization on blanket trait impls can be emulated by using two traits: one for the blanket impl, and one for the specialization.
While try_as_dyn also exists, I think this approach supercedes it. The traits do not need to be dyn compatible, so traits like Default or bytemuck marker traits like Zeroable can be specialized upon.