How to create a function that takes different function types?

I have a builder API (kind of like the bon crate), and one of my functions looked like this below. My goal was to create something like a Java/C++ “overload”, where a single name (fun) was used to give it one of a few different kinds of functions (expressed in the enum).

impl Builder {
    fn fun(mut self, f: impl Into<Fun>) -> Self {
        self.fun = Some(f.into()); self // standard builder pattern
    }
}
enum Fun {
    NoArg(Box<dyn FnOnce() -> () + 'static>),
    CtxArg(Box<dyn FnOnce(&mut Ctx) -> () + 'static>),
}

Problem is I can’t implement From like this:

impl<F> From<F> for Fun where F : FnOnce() -> () + 'static {
    fn from(value: F) -> Self {
        Fun::NoArg(Box::new(value))
    }
}
impl<F> From<F> for Fun where F : FnOnce(&mut Ctx) -> () + 'static {
    fn from(value: F) -> Self {
        Fun::CtxArg(Box::new(value))
    }
}

Error:

error[E0119]: conflicting implementations of trait `From<_>` for type `Fun`
  --> ui/src/run_on_main.rs:43:1
   |
38 | impl<F> From<F> for Fun where F : FnOnce() -> () + 'static {
   | ---------------------------------------------------------- first implementation here
...
43 | impl<F> From<F> for Fun where F : FnOnce(&mut Ctx) -> () + 'static {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Fun`

Is there a way I can write fn fun(…) as described?

Someone on discord suggested the following, but I don’t understand the suggestion. I asked the question here so there is more time and space to discuss.

When implementing the trait, you define an extra generic type for the trait. This type is a marker that specifies which impl to use. When you call the function, rust might be able to infer this extra generic type.

they meant something like the following

trait IntoFun<Args> {
    fn into_fun(self) -> Fun;
}
impl<F : FnOnce() + 'static> IntoFun<()> for F{
    fn into_fun(self) -> Fun  { Fun::NoArg(Box::new(self))}
}
impl<F : FnOnce(&mut Ctx) + 'static> IntoFun<(Ctx,)> for F {
    fn into_fun(self) -> Fun  { Fun::CtxArg(Box::new(self))}
}

impl Builder {
    fn fun<Args>(mut self, f: impl IntoFun<Args>) -> Self {
        self.fun = Some(f.into_fun()); self // standard builder pattern
    }
}

the " extra generic type for the trait." is Args.

as it was mentionned, a type could implement both FnOnce() and FnOnce(&mut Ctx). if such a type were fed to fun, then the compiler would ask you to specify Args.
but given no closure, function item or function pointer does this atm, the compiler should be able to guess what Args is every time

3 Likes