0

I'm trying to implement the Add trait for anything that implements another trait (in the example code the Test trait). I'm using references in the Add implementation because not everything that implements Test will be the same size. The code below compiles fine:

use std::ops::Add;

struct Foo(i32);
struct Bar(i64);

trait Test {}

impl Test for Foo {}
impl Test for Bar {}

impl<'a, 'b> Add<&'b Test> for &'a Test {
    type Output = Box<Test>;

    fn add(self, other: &'b Test) -> Box<Test> {
        if true {
            Box::new(Foo(5))
        } else {
            Box::new(Bar(5))
        }
    }   
}

When I try to actually use Add, as below, it says that the operation could not be applied because the implementation of Add for &Foo is missing.

fn test_add() {
    &Foo(5) + &Bar(5)
}

Have I defined the implementation incorrectly? Have I called it incorrectly? The goal is to make the function add take two references to objects which both implement Test, and return a reference (or box) to a new object that implements Test (and might not be the same underlying type as either of the inputs).

Simson
  • 3,373
  • 2
  • 24
  • 38
Alsafi
  • 53
  • 1
  • 6
  • Did you mean `T: Test` in these cases? When you write `&'a Test` it's the same as `&'a dyn Test`, a trait object. – Peter Hall Aug 15 '18 at 23:15
  • Peter Hall I tried using impl, but that caused a problem because I might not want to return the same type as the inputs. The only guarantee is that the output also implements Test. Perhaps I'm not understanding you correctly. I'll have to look up dyn. – Alsafi Aug 15 '18 at 23:20
  • `dyn` is just newer syntax for what you've written, which emphasises that the concrete type is not known at compile-time. – Peter Hall Aug 15 '18 at 23:30
  • Possible duplicate of [I implemented a trait for another trait but cannot call methods from both traits](https://stackoverflow.com/questions/29256519/i-implemented-a-trait-for-another-trait-but-cannot-call-methods-from-both-traits) – trent Aug 15 '18 at 23:56
  • Actually, [Can't implement a trait I don't own for all types that implement a trait I do own](https://stackoverflow.com/questions/43989065/cant-implement-a-trait-i-dont-own-for-all-types-that-implement-a-trait-i-do-ow?noredirect=1&lq=1) might be more precise – trent Aug 15 '18 at 23:57

2 Answers2

1

I found another approach that changes the behavior slightly, but works.

struct V<T>(T);

use std::ops::Add;
impl<T1: Test, T2: Test> Add<V<Box<T2>>> for V<Box<T1>> {
    type Output = V<Box<Test>>;
    fn add(self, other: V<Box<T2>>) -> Self::Output {
        unimplemented!()
    }
}

That allows it to return any type that implements Test, at the cost of wrapping everything in a Box and a dummy struct V. Not very elegant, and I still don't understand why my original code doesn't work, but at least this has the behavior I wanted.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alsafi
  • 53
  • 1
  • 6
  • 1
    You can combine this with your original approach to avoid `Box`ing everything: `impl<'a, 'b, T1: Test, T2: Test> Add> for V<&'a T1>` – MB-F Aug 16 '18 at 09:04
1

The problem is that the compiler cannot implicitly convert from &Foo into &Test. If you explicitly convert it into &Test first, then the operator overloading works:

fn test_add() {
    let foo: &Test = &Foo(5);
    foo + &Bar(5);
}

Alternatively, you can use the fully qualified syntax:

fn test_add() {
    <&Test as Add<&Test>>::add(&Foo(5), &Bar(5));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Scott Maddox
  • 129
  • 7