How can I store `Fn()` items in a `Box`?

I am writing a tool that analysis code. I am currently trying to test all possible syntax that can store then call a function. There are a few syntax that I don't understand how I should write them.

fn foo() {}
fn get_foo_impl() -> impl Fn() {
    foo
}

fn bar() {}
fn get_bar_ptr() -> fn() {
    bar
}

fn call_mixed_source<
    Fct: Fn(),
    GetFctTrait: Fn() -> Fct,
    // GetFctImpl: Fn() -> impl Fn(), // Not allowed yet
    GetFctDyn: Fn() -> dyn Fn(),
>(
    f1: Fct,
    f2: impl Fn(),
    f3: &dyn Fn(),
    f4: fn(),

    get_trait_trait: GetFctTrait,
    get_impl_trait: impl Fn() -> GetFctTrait,
    get_dyn_trait: &dyn Fn() -> GetFctTrait,
    get_fn_trait: fn() -> GetFctTrait,

    get_trait_dyn: GetFctDyn,
    get_impl_dyn: impl Fn() -> dyn Fn(),
    get_dyn_dyn: &dyn Fn() -> dyn Fn(),
    get_fn_dyn: fn() -> dyn Fn(),

    random: usize)
{
    let fct: Box<dyn Fn()> = match random {
        0 => Box::new(foo),
        1 => Box::new(bar),
        2 => Box::new(get_foo_impl()),
        3 => Box::new(get_bar_ptr()),

        4 => Box::new(f1),
        5 => Box::new(f2),
        6 => Box::new(f3),
        7 => Box::new(f4),

        8 => Box::new(get_trait_trait()),
        // 9 => Box::new(get_impl_trait()),
        // 10 => Box::new(get_dyn_trait()),
        // 11 => Box::new(get_fn_trait()),

        12 => Box::new(&get_trait_dyn()),
        13 => Box::new(&get_impl_dyn()),
        // 14 => Box::new(get_dyn_dyn()),
        15 => Box::new(&get_fn_dyn()),

        _ => unimplemented!(),
    };
    fct()
}

I can't figure how I should write the version 9, 10, 11 and 14. And btw, did I forget other way to call a function (except directly calling it of course!)?

I don’t know if you need to account for it or not, but all of these can be used with arbitrary subtraits of Fn() as well:

trait MyFn: Fn() {}

fn test(f: &dyn MyFn) {
    f()
}

There’s also the possibility of a custom struct involved:


struct Wrapper<F>(F);

impl<F> Wrapper<F> {
    pub fn call_1(&self) where F:Fn() {
        self.0()
    }
}


impl<F> Wrapper<F> where F:Fn() {
    pub fn call_2(&self) {
        self.0()
    }
}


impl<F> std::ops::Deref for Wrapper<F> {
    type Target = F;
    fn deref(&self)->&F { &self.0 }
}

fn test(x: Wrapper<impl Fn()>) {
    x()
}

On the nightly compiler, you can use fully-qualified trait method syntax:

#![feature(fn_traits)]

fn test(f: &impl Fn()) {
    Fn::call(f,())
}

fn test2<F:Fn()>(f:F) {
    F::call(&f, ())
}
1 Like

Every case where you have a -> dyn Fn() doesn’t make any sense. Unsized return types are not (yet) an option in Rust. For your problems with 9, 10, and 11, the problem is that you have a function like e.g. impl Fn() -> (impl Fn() -> impl Fn()) and call that one to get some impl Fn() -> impl Fn(). Put into a Box, this cannot convert to a dyn Fn() since it still has a non-() return type. You could call it "another time" to get some impl Fn() before putting it into the Box, like e.g.

9 => Box::new(get_impl_trait()()),
1 Like

My reaction was something like "pfffffffffffffffffffffffffff"! I should have used impl Fn() -> Fct and not impl Fn() -> GetFctTrait and the like. Thanks!

And thanks @2e71828 for the extra examples, I need to add them.

Here’s one last possibility from me. There’s a combinatorial explosion of these, though; at some point, you’ll need to rely on whatever system you’re using for type inference to tell you that something is callable.

trait WithCallback {
    type CB: Fn();
    fn get_cb(&self)->Option<&Self::CB>;
}

fn test(x:impl WithCallback)->Option<()> {
    Some(x.get_cb()?())
}
1 Like

My analysis is based on MIR, so I think I'm good, I just want to be sure that it work for most variations.

@2e71828 in your first post, you duplicated the call_2. Did you had a 3rd idea? Or is this just an extra copy-paste?

That’s just a copy-paste error. I’ll edit my post to remove it. I did forget one of the fn_traits variants, though:

fn test(x:impl Fn()) {
    x.call(())
}

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.