Search code examples
genericsrustassociated-types

How to provide a proper type for use of generic impl?


I'm trying to build up my intuition for associated types in Rust. In my experiments I build this (nonsensical) combination of generics and impl.

struct MyStruct<T>{
    t:T
}

trait MyTrait {
    type T1;
    fn tt(x:Self::T1) -> MyStruct<Self::T1>;
}

impl<T> MyTrait for MyStruct<T> {
    type T1=T;
    fn tt(a:Self::T1) -> MyStruct<Self::T1> {
        MyStruct{t:a}
    }

}

It compiles, but the problem is that I can't use 'tt' function.


fn main() {
    let x = MyTrait::tt(1);
}

causing an error: cannot infer type.

I've tried my options to provide type hints for Rust, but more I try the less audible (for me) error messages become.

Can someone provide me with example of use MyTrait::tt function from above, or is this a dead end? If so, why?


Solution

  • The trouble with using something like MyTrait::tt(1) is that the actual type that implements MyTrait can't be deduced from the argument 1. You could, for example, implement MyTrait for String, but make T1 be i32. With that setup, MyTrait::tt(1) could refer to <String as MyTrait>::tt (using the fully qualified method call syntax). On the other hand, you could also implement MyTrait for i32 with T1 = i32. Then MyTrait::tt(1) could also refer to <i32 as MyTrait>::tt.

    impl MyTrait for String {
        type T1 = i32;
        fn tt(x: i32) -> MyStruct<i32> {
            MyStruct { t: x }
        }
    }
    
    impl MyTrait for i32 {
        type T1 = i32;
        fn tt(x: i32) -> MyStruct<i32> {
            MyStruct { t: x }
        }
    }
    
    fn main() {
        let x = 47_i32;
    
        // let y = MyTrait::tt(x);
        // That could refer to either of the following
        let y = <String as MyTrait>::tt(x);
        let z = <i32 as MyTrait>::tt(x);
    
        // Of course, you don't need the fully qualified syntax
        // all the time.
        let y2 = String::tt(x);
        let z2 = i32::tt(x);
    }
    

    (playground)

    This behavior might be improved with smarter trait solvers since in your particular case, the only applicable implementation is the one on MyStruct<i32>. But for now, I'd just use <MyStruct<i32> as MyTrait>::tt(1) or simply MyStruct::tt(1).

    fn main() {
        let x = <MyStruct<i32> as MyTrait>::tt(1);
        let x = MyStruct::tt(1);
    }
    

    (playground)

    To learn more about traits and fully qualified syntax take a look in The Book's chapter on advanced traits.