Using Self as a generic type parameter in a trait

Hi,

I have a struct which takes a generic type parameter and I want to create a trait which provides a function which takes self and puts this self into the generic type parameter field of my struct.

The goal is to implement the trait i.e. for String.

But when I compile I get the error about the incorrect type parameter. See below.

struct MyStruct<T> {
    text: T,
    id: usize
}

impl<T> MyStruct<T> {
    fn new(text: T, id: usize) -> Self {
        Self { text, id }
    }
}

trait MyTrait {
    fn foo<T>(self, id: usize) -> MyStruct<T> 
    where Self: Sized
    {
        MyStruct::new(self, id)
    }
}

fn main() {
    let _ = MyStruct::new("abc", 1);
}
   


   
   

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:16:23
   |
12 | trait MyTrait {
   | ------------- found type parameter
13 |     fn foo<T>(self, id: usize) -> MyStruct<T> 
   |            - expected type parameter
...
16 |         MyStruct::new(self, id)
   |         ------------- ^^^^ expected type parameter `T`, found type parameter `Self`
   |         |
   |         arguments to this function are incorrect
   |
   = note: expected type parameter `T`
              found type parameter `Self`
   = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
note: associated function defined here
  --> src/main.rs:7:8
   |
 7 |     fn new(text: T, id: usize) -> Self {
   |        ^^^ -------

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 1 previous error

You can use Self,which is the type of self, instead making a generic function.

    text: T,
    id: usize
}

impl<T> MyStruct<T> {
    fn new(text: T, id: usize) -> Self {
        Self { text, id }
    }
}

trait MyTrait {
    fn foo(self, id: usize) -> MyStruct<Self> 
    where Self: Sized
    {
        MyStruct::new(self, id)
    }
}

fn main() {
    let _ = MyStruct::new("abc", 1);
}```
2 Likes

Ok, thanks.

In case you want an explanation, the meaning of this API is:

trait MyTrait {
    fn foo<T>(self, id: usize) -> MyStruct<T> 

The caller chooses T and gets a MyStruct<T> back. Since there are no arguments to infer T from, they'd call it like...

implementor.foo::<String>(0);
let mss: MyStruct<String> = implementor.foo(0);

And in the example, the implementation must return MyStruct<String>, since that's what the caller chose.

Every <Implementor as MyTrait>::foo has to be able to handle every choice of T.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.