Specify Self as associated type of return impl trait

I was playing around with returning impl traits and ran into this issue:

use ::std::ops::Add;
use ::std::fmt::Debug;

fn get_num() -> impl Add + Copy + Debug {
    1
}

fn main() {
    let x = get_num();
    let y = get_num();
    let z = x + y;
    println!("{:?}", z);
}

error[E0277]: `<impl Add+Copy+Debug as Add>::Output` doesn't implement `Debug`
  --> src/main.rs:12:22
   |
12 |     println!("{:?}", z);
   |                      ^ `<impl Add+Copy+Debug as Add>::Output` cannot be formatted using `{:?}` because it doesn't implement `Debug`
   |

Seems the type checker has lost track of the fact that Add::Output = Self for i32. Fair enough, I should've been more specific. However when I tried to remedy the problem by changing impl Add to impl Add<Output = Self>, the code instead fails with:

error[E0411]: cannot find type `Self` in this scope
 --> src/main.rs:4:35
  |
4 | fn get_num() -> impl Add<Output = Self> + Copy + Debug {
  |                                   ^^^^ `Self` is only available in impls, traits, and type definitions

So here's my question: Is there a particular reason why this can't work, or is this just a matter of no one implementing it yet? What are the best workarounds?

As far as I can see the only workaround is to introduce a new trait (e.g. trait ClosedAdd: Add<Output = Self> {}) and use it in the return type instead of Add. This does work, but it somewhat defeats the purpose of being able to specify ad-hoc combinations of traits. If I have to introduce a new trait just to get the type checker to see that Add::Output = Self I might as well roll Copy, Debug and whatever else into the new trait as well.

Another thing I tried was -> impl Add<Output = Add + Copy + Debug> + Copy + Debug, but this isn't quite the same. It works insofar as the output of Add will also implement the traits, but the type checker can't prove it's the same type as impl Add.

To be clear, I'm not trying to solve a particular problem here. I'm just new to Rust and wanted to understand the language better. Thanks for your time.

Yes. Consider this code:

impl SomeStruct {
    fn get_num() -> impl Add<Add::Output = Self> + Copy + Debug {
        1
    }
}

The Self keyword would in this situation refer to SomeStruct rather than whatever the method is returning. You would need a new keyword to do it.

1 Like

Makes sense. I figured there had to be a good reason but I couldn't figure it out. Thanks!