Can I make a method that accepts two different callback types

So I have an existing method that accepts FnOnce(Foo), and I'd like to change it so that it also accepts FnOnce(Foo) -> Result<()>. I can add a new Callback enum param that can store either of those. E.g:

pub enum Callback {
    FnOnce(Box<dyn FnOnce(&mut Foo)>),
    FnOnceResult(Box<dyn FnOnce(&mut Foo) -> Result<()>>),
}

And I can change the method to accept T: Into<Callback>, but I'm struggling to work out how to make create the into methods as this gives me a conflicting implementation error:

impl<T: FnOnce(&mut Foo) + 'static> From<T> for Callback {
    fn from(f: T) -> Self {
        Callback::FnOnce(Box::new(f))
    }
}

impl<T: FnOnce(&mut Foo) -> Result<()> + 'static> From<T> for Callback {
    fn from(f: T) -> Self {
        Callback::FnOnceResult(Box::new(f))
    }
}

Is there any way that would make this work easily.

Playground: Rust Playground

(The context for this is Ratatui's Terminal::draw method, which would be nice to accept a method that returns a result instead of having to panic. I can of course introduce a try_draw method, but i was curious about whether it's possible to make the existing method work for both method types)

The moment you start having multiple implementations involving generic types in the same position, you're better off finding some other way of solving the problem. In this case, I'm not aware of any viable solution to get what you want.

It will probably be much easier to simply put Ok(()) at the end of the existing closures. If you really want to support both, I'd leave the From implementation for the more common of the two, and have a function to do the conversion explicitly. i.e. something like Callback::with_result(some_closure).

1 Like

:clap: stop :clap: using :clap: enums :clap: for :clap:callbacks!

Seriously, enums are not for storing a bunch of unrelated, arbitrary functions, and are not a substitute for generics.

The trick is to notice that you can still implement different instantiations of a generic trait, because they are different traits.

Playground.

3 Likes

Thanks :slight_smile: