[solved] Generics and associated types


#1

Hello,

I’m currently trying to solve coding puzzles from Advent of Code (i’m not gonna ask for a solution here :wink: ), using Rust so i can learn more about the language. For now, even if it’s more verbose than, say, Python, i really enjoy it so i’ll continue.

And now, on a given problem, i thought i could go further using Rust and use generics in a more “advanced” way. I’m currently stuck on a pure Rust problem. Consider this code:

use std::ops::Add;

pub struct Remote<T> {
    x: T,
}

pub struct Local<S> {
    x: S,
}

pub struct Adder<T> {
    local: Local<T>
}

impl<I: Add> Adder<I> {
    pub fn add(&self, r: Remote<I>) -> I {
        self.local.x + r.x
    }
}

fn main() {
    let adder: Adder<u64> = Adder{local: Local{x:1}};
    let remote: Remote<u64> = Remote{x:2};

    adder.add(remote);
}

Before even trying to compile, i smelled that something could go wrong with this. I want to add “types” S and T… but they aren’t “linked” together in any way… but as i’m not sure about what the compiler is telling me

error[E0308]: mismatched types                                                                                                                                                                
  --> src/main.rs:17:9                                                                                                                                                                        
   |                                                                                                                                                                                          
16 |     pub fn add(&self, r: Remote<I>) -> I {                                                                                                                                               
   |                                        - expected `I` because of return type                                                                                                             
17 |         self.local.x + r.x                                                                                                                                                               
   |         ^^^^^^^^^^^^^^^^^^ expected type parameter, found associated type                                                                                                                
   |                                                                                                                                                                                          
   = note: expected type `I`                                                                                                                                                                  
              found type `<I as std::ops::Add>::Output`

So aaah… associated type? I tried to do some messy stuff with a second type parameter (named C) on Adder, using PhantomData to actually do something with it so the compiler doesn’t yell at me, and using r: Remote<C> but it doesn’t work any more.

Suddenly I remembered that i could use C=Remote<I> but then the compiler say something like “deprecated” so it’s a no go either.

How could i achieve to get the same type for Remote and Local?

Thanks


#2

You want the following (note the return type change):

pub fn add(&self, r: Remote<I>) -> I::Output

You’ll also likely want to add a Copy bound on I (ie I: Add + Copy).


#3

Wow… that’s pretty obvious now.

Reading the documentation of std::ops::Add about type Output makes it much clearer now.

Nonetheless i’m not sure how that part works. Could you please point me where in Rust’s book(s) i can learn about it?

Many thanks.


#4

Which bit specifically? The Output associated type of the Add trait? ie what are associated types?


#5

Yes, about associated types :slight_smile:


#6

Probably here (surprised it’s in the “advanced” section, and not in the generics chapter).


#7

Interesting readings on the way. Thanks again for your precious help :slight_smile: