Helping the compiler to infer types

Consider the following type family:

trait Throttler<Request> {
    type Resource;
    
    fn acquire(&self, req: Request) -> Self::Resource;
    
    fn map<F, Resource>(self, f: F) -> Map<Self, F>
    where
        Self: Sized,
        F: FnOnce(Self::Resource) -> Resource,
    {
         Map {
            f,
            inner: self,
        }
    }
}

pub struct Map<T, F> {
    inner: T,
    f: F,
}

impl<Request, T, F, Resource> Throttler<Request> for Map<T, F>
where
    T: Throttler<Request>,
    F: FnOnce(T::Resource) -> Resource + Clone,
{
    type Resource = Resource;

    fn acquire(&self, request: Request) -> Self::Resource {
        (self.f.clone())(self.inner.acquire(request))
    }
}

A Throttler allows acquiring a given Resource for some Request type; it's possible to Map a from one Resource type to another.

Consider the following MyThrottler type, which implements Throttler for some generic Request type, but with additional constraints:

struct MyThrottler { }

trait MyThrottlerRequest { }

impl<R: MyThrottlerRequest> Throttler<R> for MyThrottler<R> {
    type Resource = ();
    
    fn acquire(&self, req: R) -> Self::Resource { }
}

When using these types like so:

let t: Box<dyn Throttler<Req, Resource = Vec<()>>> = Box::new(MyThrottler { }.map(|_: ()| Vec::new()));

rustc complains:

error[E0282]: type annotations needed
  --> src/main.rs:56:82
   |
56 |     let t: Box<dyn Throttler<Req, Resource = Vec<()>>> = Box::new(MyThrottler {}.map(|_: ()| Vec::new()));
   |                                                                                  ^^^
   |
help: try using a fully qualified path to specify the expected types
   |
56 |     let t: Box<dyn Throttler<Req, Resource = Vec<()>>> = Box::new(<MyThrottler as Throttler<Request>>::map::<[closure@src/main.rs:56:86: 56:93], Vec<()>>(MyThrottler {}, |_: ()| Vec::new()));

This can be solved by adding a generic parameter to MyThrottler, which requires using PhantomData:

struct MyThrottler<R: MyThrottlerRequest> {
    _marker: std::marker::PhantomData<R>,
}

Rust playground link.

Is there a more elegant way to get the compiler to infer the types without needing to use PhantomData?

Fully qualified function call syntax is the way to do this kind of thing in general.

You can actually just specify the trait type and allow the struct type to be inferred instead in this case though.

Throttler::<Req>::map(MyThrottler, |_: ()| Vec::new())

In more complicated situations you might need to use fully qualified syntax which would be

<MyThrottler as Throttler<Req>>::map(
    MyThrottler,
    |_: ()| Vec::new(),
)
3 Likes

Ah-ha, I see. Many thanks!