Generics ending up expecting a Type, not a Struct

Hi. I've managed to tie myself into a generics knot. All my reasoning says the below should compile, but it gives me an error that the impl for AnotherC should be returning a Type, not a Struct. I can't see what I've got wrong.

This is all ending up a bit complex, though and I wonder if there's a more rust-y way to achieve what I want. I have objects 'A's that identified by ids 'B's that are essentially different newtypes around strings most of the time. I have events 'C's that aggregate together to build objects 'A's. I want to enforce that an event 'C' that will aggregate to a given type of object 'A' can only take an appropriate id type 'B'.

Thanks for any help/advice.

trait A {
    fn get_string() -> String {
        "Hi. I'm an A".to_string()
    }
}

trait B<T: A> {
    fn get_string() -> String {
        "Hi. Iam a B".to_string()
    }
}

trait C<T: A> {
    fn get_thing<Y: B<T>>(&self) -> Y;
}

struct AnA {}
impl A for AnA{}

struct SomeB{}

impl B<AnA> for SomeB{}

struct AnotherC {
    thing: SomeB,
}

impl C<AnA> for AnotherC {
    fn get_thing<SomeB>(&self) -> SomeB{
        self.thing
    }
}

fn main() {
    let c = AnotherC {
        thing: SomeB{}
    };
    println!("{}", c.get_thing());
}

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:30:9
   |
29 |     fn get_thing<SomeB>(&self) -> SomeB{
   |                  -----            ----- expected `SomeB` because of return type
   |                  |
   |                  this type parameter
30 |         self.thing
   |         ^^^^^^^^^^ expected type parameter `SomeB`, found struct `SomeB`
   |
   = note: expected type parameter `SomeB` (type parameter `SomeB`)
                      found struct `SomeB` (struct `SomeB`)

error[E0282]: type annotations needed
  --> src/main.rs:38:22
   |
38 |     println!("{}", c.get_thing());
   |                      ^^^^^^^^^ cannot infer type for type parameter `Y` declared on the associated function `get_thing`
   |
help: consider specifying the type argument in the method call
   |
38 |     println!("{}", c.get_thing::<Y>());
   |                               ^^^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0282, E0308.
For more information about an error, try `rustc --explain E0282`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

You are mixing up "for all types that implement B<AnA>" and "for a specific type that implements B<AnA>". When you use generics, the function must be able to be used with any type Y as long as it satisfies the : B<T> bound, but you are trying to implement it for only a specific type.

To change it from "for all types" to "for a specific type", you can use an associated type:

trait C<T: A> {
    type Y: B<T>;

    fn get_thing(&self) -> Self::Y;
}

then the impl becomes

impl C<AnA> for AnotherC {
    type Y = SomeB;

    fn get_thing(&self) -> SomeB {
        self.thing
    }
}
3 Likes

In function like get_thing<B>(), the B is an argument chosen by the caller of the function. The caller is free to demand any type, even a custom type that they'll define. Therefore returning a struct of a specific type will not satisfy demand for an arbitrary type that you don't control.

2 Likes

For examples of what @alice and @kornel are talking about, compare str.parse() in the standard library, which parses to any type the caller chooses, so long as it implements FromStr, and Iterator::next(), which returns an item of a particular type chosen by the particular implementor of the Iterator trait.

1 Like

I understand now. Thank you all

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.