The following code does not compile:
/// Commutative operation.
///
/// Must be implemented for both sides.
/// AutoCommutativeOp may be implemented for one side only, and it will automatically implement CommutativeOp for both sides.
pub trait CommutativeOp<Rhs> {
fn op(self, rhs: Rhs);
}
/// Automatically implements CommutativeOp for the other side.
pub trait AutoCommutativeOp<Rhs> {
fn op(self, rhs: Rhs);
}
impl<Lhs, Rhs> CommutativeOp<Rhs> for Lhs
where
Lhs: AutoCommutativeOp<Rhs>,
{
fn op(self, rhs: Rhs) {
self.op(rhs)
}
}
impl<Lhs, Rhs> CommutativeOp<Rhs> for Lhs
where
Rhs: AutoCommutativeOp<Lhs>,
{
fn op(self, rhs: Rhs) {
rhs.op(self)
}
}
The compiler says error[E0119]: conflicting implementations of trait `CommutativeOp<_>`
. I do not understand why that would be illegal.
Imagine I implement AutoCommutativeOp<B> for A
. That means the first impl matches the following:
impl CommutativeOp<B> for A
//where
// A: AutoCommutativeOp<B>,
{
...
}
and the second impl
matches the following:
impl CommutativeOp<A> for B
//where
// A: AutoCommutativeOp<B>,
{
...
}
That seems to be a pretty reasonable constraint and I don't see what causes the incompatibility. Have I overlooked something?
This is neat! I'm not sure there's a way to accomplish what you're trying to accomplish using auto trait implementations, though, because of the "Lhs==Rhs" problem where your auto trait impl redefines its own implementation.
Consider using a macro instead? Then your macro can just define both Lhs op Rhs and Rhs op Lhs at the same time. I've also written an output type generic into this to make it a little more useful.
macro_rules! commutative_op {
($Lhs:ty, $Rhs:ty, $O:ty, $body:expr) => {
impl CommutativeOp<$Rhs, $O> for $Lhs {
fn op(self, rhs: $Rhs) -> $O {
$body(self, rhs)
}
}
impl CommutativeOp<$Lhs, $O> for $Rhs {
fn op(self, lhs: $Lhs) -> $O {
$body(lhs, self)
}
}
};
}
commutative_op!(&str, usize, String, |a: &str, b: usize| a.repeat(b));
let res = "something".op(2usize);
let res2 = 2usize.op("something");
assert_eq!(res, res2)