Accept closure of variable arity as argument

I have a function and I'd like to accept closures with different sets of arguments and return value types. For example would it be possible to create a trait Func.

trait Func {
    fn func(&self, input: Input) -> Output;
}

which could be implemented for a set of functions:

Fn(A, B, C, ..) -> R

Where

  • A, B, C, ... each implement FuncArg
  • R implements FuncReturn

So these functions would all have arguments implementing a trait FuncArg and a return value implementing FuncReturn where these traits convert the concrete Input type to a set of impl FuncArg and a concrete Output type from the impl FuncReturn.

Is this kind of API possible in Rust? Could anyone point me to some example code where anything similar has been done before?

There's no direct support for this, yet (look for "variadic generics" if you want to know the status of this feature). In the meantime, you can just accept a single-argument closure and pass a tuple.

4 Likes

Okay so I managed to get something working. However, it only works for owned arguments. Is there a way for me to get the lifetimes to work?

Playground

error: implementation of `Function` is not general enough
  --> src/lib.rs:16:5
   |
16 |     takes_any_function(f);
   |     ^^^^^^^^^^^^^^^^^^ implementation of `Function` is not general enough

This is a well-known limitation of closures: they cannot satisfy a for<'a> Trait<'a> bound where Trait<'a> requires Fn(&'a T). Here, the compiler can infer that the type of f implements Function<'a, String, (&'a str,)> for any particular lifetime 'a. This allows the explicit Function::call_func(&f, args) in main() to work with no issues. However, f cannot be passed to takes_any_function(), since f's type does not implement for<'a> Function<'a, String, (&'a str,)>. Currently, the only workaround is to use a lifetime-generic fn item instead of a closure. See also RFC 3216 for a proposed language solution to this issue.

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.