Cannot invoke derefenced boxed FnOnce, dyn FnOnce()

Hi, I'm facing some weird issue which I can't seem to find the way out. I'm fairly new to Rust so any advice would be greatly appreciated.

Question 1

Is there a way to invoke a boxed-then-referenced function, as in &Box<dyn FnOnce()>? The compiler says that it can't move a value out of type dyn FnOnce().

    let f: Box<dyn FnOnce()> = Box::new(|| println!("OK\n"));
    let b = &*f;
    b(); // cannot invoke. Doesn't compile.

Question 2 (The root cause)

This is where I've come across the issue. I'm looking to create a singleton vector of functions. I know this involves so many unsafe hackeries but I just want to figure out as much as possible what I can/cannot do with Rust. Would there be a way to call the functions below?

static mut V: *const Vec<Box<dyn FnOnce()>> = 0 as *const Vec<Box<dyn FnOnce()>>;

    unsafe {
        // ...V is then modified and has some functions as elements

        // Now I would like to call those element functions
        for e in &*V {
            let f = &*e;
            f(); // cannot move out of *f which is behind a shared reference
        }
    }

No, not if it's FnOnce. Those can only be called once, i. e. you need to own them before the call and give up ownership with the call. The &… reference type, i. e. a shared/immutable reference is only a borrrow. It can be duplicated and the place you borrow from wants its ownership back afterwards. Thus this kind of access to a function that can only be called once isn't suitable in order to be able to call that function.

If you need to work with functions behind shared references, either consider using dyn Fn instead of dyn FnOnce. Or try to gain ownership of the function before calling it.

There's no need to use static mut in Rust or any unsafe for this. Try using something like

static V: Lazy<Mutex<Vec<Box<dyn FnOnce()>>>>
  = Lazy::new(|| Mutex::new(Vec::new()));

instead. With once_cell::Sync::Lazy and std::sync::Mutex.

Access the vec inside of the mutex by obtaining a guard from locking it first (let guard = V.lock().unwrap();). You can then e. g. pop a function to gain ownership (if let Some(f) = guard.pop() { … }) and call it (if calling every function at most once is what you're after, otherwise, as mentioned, consider dyn Fn, or perhaps dyn FnMut).

4 Likes

Such a priceless answer. Yes, dyn Fn was the way to go among others. Thanks for that.

And I've also looked into once_cell and asked myself why it hasn't been part of the standard library. Well, that might be the case down the road. I like that it allows us to define and use the lazily initialized value in a "safe" way.

To make it compile, I've also specified a trait Send and it became something of following nature, for those who might be interested.

pub static V: Lazy<Mutex<Vec<Command>>> = Lazy::new(|| {
    let v = vec![
        Strt {
            f1: Box::new(function1),
            f2: Box::new(fucntion2),
        },
    ];
    Mutex::new(v)
});

pub struct Strt {
    pub f1: Box<dyn Fn() -> () + Send>,
    pub f2: Box<dyn Fn() -> () + Send>,
}

fn f() {
    let v = V.lock().unwrap();
    for e in v.iter() {
        (&e.f1)(); 
    }
 }
1 Like

Ah right, the Send. Hadn't tested my code, but I guess the compiler helps out nicely with this, too. By the way, when the return type is (), you don't need to write it. So Fn() -> () can be abbreviated as just Fn().

1 Like