How to provide a generic type for Fn trait?

Hello fellow Rustaceans.

I'm struggling to get certain traits to work correctly, and it'll be great if someone could point me to the right direction on how to handle this appropriately.

pub struct ServableFn<F, T>
where
    F: Fn(Input<T>) -> Result<Box<dyn Any>, String>,
    T: Serialize,
{
    pub opts: FunctionOps,
    pub trigger: Trigger,
    pub func: F,
}

I have this struct where I want func to be a closure where it accepts an Input<T> which is a generic struct.
Here's what Input<T> looks like:

#[derive(Deserialize)]
pub struct Input<T> {
    pub event: T,
    pub events: Vec<T>,
    pub ctx: InputCtx,
}

#[derive(Deserialize)]
pub struct InputCtx {
    pub fn_id: String,
    pub run_id: String,
    // pub step_id: String,
}

The error I'm getting from the compiler is this.

error[E0392]: parameter `T` is never used
  --> src/function.rs:57:26
   |
57 | pub struct ServableFn<F, T>
   |                          ^ unused parameter
   |
   = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`

For more information about this error, try `rustc --explain E0392`.

Which rightfully claims that T is not used. However, if I remove T from the struct signature,

Fn(Input<T>) -> Result<Box<dyn Any>, String>,

will now start failing because it can't see T in scope anymore.
I gave PhantomData a try but it makes the code unnecessarily more complicated, and that field is not going to be used so I rather not have it.

How would one approach this issue?

Code is available here.

And this is the unsuccessful changes so far.

Any guidance are appreciated.
Thank you!

You can use PhantomData to make the compiler think you are using T in ServableFn:

pub struct ServableFn<F, T>
where
    F: Fn(Input<T>) -> Result<Box<dyn Any>, String>,
    T: Serialize,
{
    pub opts: FunctionOps,
    pub trigger: Trigger,
    pub func: F,
+    _marker: PhantomData<T>,
}

Playground.

1 Like

Generally, I'd choose PhantomData<fn() -> T> for such cases, to avoid unnecessary extra auto trait bounds. (Or maybe PhantomData<fn(T)> in this cause? Or maybe, to be invariant, doing PhantomData<fn(T) -> T>.)

Also, there is the question to be asked why the Fn bound needs to be on the struct at all - if it's only on the methods that use it, the struct wouldn't need T as a parameter at all.

6 Likes

@steffahn @jofas
Thank you for your replies. After digging around further, it seems like the path I was trying to go down is not allowed by the rust compiler in the first place.
I've since hit a separate issue so closing this one off.

Appreciate the answers. :slight_smile: