To give some code example, I imagine something like
trait template SumByAdd: Add<Output = Self> + Sized {
fn zero() -> Self;
}
impl<T> Sum<T> for T
where
T: SumByAdd,
{
fn sum<I>(mut iter: I) -> Self
where
I: Iterator<Item = A>
{
iter.reduce(T::add).unwrap_or_else(SumByAdd::zero)
}
}
trait template SumByAddDefault: Add<Output = Self> + Default + Sized {}
impl<T: SumByAddDefault> SumByAdd for T {
fn zero() -> Self {
T::default()
}
}
can be defined in any crate, and users of that crate could then write
#[derive(Debug, PartialEq, Eq)]
enum DataSize {
Fixed(usize),
Variable,
}
impl Default for DataSize {
fn default() -> Self {
DataSize::Fixed(0)
}
}
impl Add for DataSize {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
use DataSize::*;
match (self, rhs) {
(Fixed(x), Fixed(y)) => Fixed(x + y),
_ => Variable,
}
}
}
impl SumByAddDefault for DataSize {} // implements Sum
or e.g.
#[derive(Debug, PartialEq, Eq)]
enum DataSize {
Fixed(usize),
Variable,
}
impl Add for DataSize {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
use DataSize::*;
match (self, rhs) {
(Fixed(x), Fixed(y)) => Fixed(x + y),
_ => Variable,
}
}
}
// implements Sum
impl SumByAdd for DataSize {
fn zero() -> Self {
DataSize::Fixed(0)
}
}
Note that such templates are implement-only; you can’t write them in bounds (outside of the defining trait) or call any of their methods. Thus switching from one template to another or a manual implementation, or the same template in a semver-breaking new version of the crate defining the template, etc… all isn’t a breaking change.
The writeup I linked also considers sealed traits, and the potential to have templates and sealed traits of the same name; this is irrelevant for this case.
You could also use this to e.g. provide a template with an add
and a zero
method that automatically implements, say, Sum
and Add
and Zero
(from num
) for you. Perhaps if you want (with a different template; i.e. everything is always explicitly opt-in), for Copy
types, even including implementations of Add
/Sum
for &T
.
Note that in my mind, the necessary extensions to coherence-checking / orphan rules that make this work should apply to all traits, not just trait template
s. The “template
” just implies the “implement-only” property described above that’s necessary to get the right stability guarantees when using such templates.