Добавлять только общие типы, которые имеют разные конкретные типы

#generics #rust

Вопрос:

Учитывая Base<Foo> и Base<Bar> , есть ли способ реализовать Add так, чтобы только Base<Foo> Base<Bar> и Base<Bar> Base<Foo> компилировать? т.е.. добавлять только, если конкретные типы разные?

В моем случае они могут быть только 2 разных типов.

Пример:

Учитывая

 #[derive(Debug)]
struct Base<T> {
    n: u32,
    _marker: std::marker::PhantomData<T>,
}

trait FooTrait {}
struct FooImpl;
impl FooTrait for FooImpl {}

trait BarTrait {}
struct BarImpl;
impl BarTrait for BarImpl {}
 

есть ли способ реализовать Add так, чтобы

 let foo: Base<Foo> = Base::new();
let foo2 = foo.clone();
let bar: Base<Bar> = Base::new();
let bar2: bar.clone();

let foobar = foo   bar;
let foobar = bar2   foo2;
 

компилируется, но

 let foo: Base<Foo> = Base::new();
let foo2 = foo.clone();
let bar: Base<Bar> = Base::new();
let bar2: bar.clone();

let foofoo = foo   foo2;
let barbar = bar   bar2;
 

не так ли?

Я пытался

 impl<A, B> Add<Base<B>> for Base<A>
where
    A: DerivedATrait,
    B: DerivedBTrait,
{
    type Output = Base<A>;

    fn add(self, rhs: Base<B>) -> Base<A> {
        Base::new(self.n   rhs.n)
    }
}
 

но это только позволяет Base<A> Base<B> , а не наоборот, и дополнительная Add реализация, меняющая границы, не компилируется.

Комментарии:

1. я не уверен, что понимаю, чего вы хотите. вам нужна игровая площадка ?

2. Если вы используете nightly, вы можете попробовать изучить функции auto_traits и negative_impls . Это должно позволить вам более точно контролировать, какие impls существуют, но я не уверен, применимы ли они к вашей конкретной проблеме.

3. @VaillantEtienne, я хочу Base<Foo> Base<Barr> и Base<Bar> Base<Foo> компилировать, но Base<Foo> Base<Foo> и Base<Bar> Base<Bar> не компилировать.

4. Вам нужны общие типы? Или вы можете просто impl Add<Base<Foo>> for Base<Bar> и impl Add<Base<Bar>> for Base<Foo> ?