This is an idea I had that I don't have the bandwidth right now to write a RFC for, so I'm just throwing it out there to see if it gets interest.
Summary
You should be able to implement multiple traits at once in a single impl block, as long as they form a supertrait chain.
Example syntax:
use std::ops::{Deref, DerefMut};
struct DerefMutExample<T> {
value: T
}
impl<T> Deref + DerefMut for DerefMutExample<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
Rationale
This would just be syntactic sugar for multiple implementation blocks.
This is especially convenient for some pair of traits (eg Deref + DerefMut, PartialEq + Eq, PartialOrd + Ord) where the second trait is basically a refinement of the first, with almost the same semantic meaning. In those cases, it makes sense to implement all the traits in the group at once, because both implementations "express" essentially the same concept.
In particular, some "refinement" traits (eg ExactSizeIterator) can be implemented without any methods. Just writing impl Iterator + ExactSizeIterator feels more expressive that having make it a second block.
Subtraits can define items with the same name as in the supertrait, so that case needs to be handled in some fashion (probably just by disallowing bundling those implementations).
Subtraits items are constrained to be "subtypes" (I don't remember the exact term) of the eponymous item in the super-trait, right?
Let's see...
trait Super {
fn foo(&self, i: i32);
}
trait Sub: Super {
fn foo(&self, x: &f64);
}
Nope. This compiles perfectly fine.
Ugh. The feature sounds a lot less elegant now.
I guess the simplest way to implement bundled implementations would be to only allow them if the trait definitions match both trait's items. That would make adding methods to the supertrait a potential breaking change though.
Does this provide any value other than syntactic sugar? I feel like the expressive value of implementing related traits simultaneously gets overshadowed by the anti-didactic value of the new construct.
This makes trait impls significantly less greppable. At the moment if I want to search for trait impls, and the trait isn't generic, I can just search TraitName for , and find all usages. With this proposal grepping for impls becomes impossible, because even if a trait has no supertraits, someone downstream can always add a subtrait, and then write impl TraitName + SubTrait for Type.
Note that greppability is still important in the age of IDEs: an IDE may still be not available in many contexts (like online code review or docs), IDEs have bugs, and it's impossible to search for impls within macros using an IDE (at best you'll get the call site of the macro, but the macro definition itself may span thousands of lines, and with proc macros it's even worse).
Given that this syntax sugar basically saves just a line of impl header (impl<Ts..> Trait<Ts..> for Type<Ts..> where conds), I say that the cost-benefit slider is heavily on the "cost" side.