Search code examples
rusttraitstype-inferenceassociated-types

Rust cannot infer type that is explicitly defined in function signature


The following code fails with a type error:

Compiling playground v0.0.1 (/playground)
error[E0282]: type annotations needed
  --> src/main.rs:41:8
   |
41 |       &produce_generic(),
   |        ^^^^^^^^^^^^^^^ cannot infer type of the type parameter `P` declared on the function `produce_generic`
   |
help: consider specifying the generic argument
   |
41 |       &produce_generic::<P>(),
   |                       +++++

For more information about this error, try `rustc --explain E0282`.
error: could not compile `playground` due to previous error

Full link to playground.

Code:

trait Produce {
    type Output<A>;
    fn produce<A>(a: A) -> Self::Output<A>;
}

struct ProduceUint;

impl Produce for ProduceUint {
    type Output<A> = u64;
    
    fn produce<A>(_a: A) -> Self::Output<A> {
        0
    }
}

struct ProduceString;

impl Produce for ProduceString {
    type Output<A> = String;
    
    fn produce<A>(_a: A) -> Self::Output<A> {
        "hello".into()
    }
}

fn produce_both<A, B>(
  produce_uint: &<ProduceUint as Produce>::Output<A>,
  produce_string: &<ProduceString as Produce>::Output<A>
) -> (u64, String) {
    (*produce_uint, produce_string.clone())
}

struct SomeType;

fn produce_generic<P: Produce>() -> P::Output<SomeType> {
    P::produce(SomeType)
}

fn main() {
    assert_eq!(produce_both::<SomeType, SomeType>(
      &produce_generic(),
      &produce_generic()
    ), (0, "hello".into()))
}

This surprised me because the function signature for produce_both is very clear about which implementation it is using. Any ideas why inference fails in this scenario and perhaps if there's an alternative way to define the functions to help the compiler infer types?


Solution

  • The compiler cannot infer "in reverse" with associated types. The fact that you're specifying in product_both the type does not matter - there could be defined another type that is also producing the same type:

    struct AnotherProduceUint;
    
    impl Produce for AnotherProduceUint {
        type Output<A> = u64;
        
        fn produce<A>(_a: A) -> Self::Output<A> {
            123
        }
    }
    

    And now the compiler cannot decide between ProductUint and AnotherProductUint.