Storing the return value from an Fn closure

The code below works if &dyn Fn(&mut [u8]) is changed to &dyn FnOnce(&mut [u8]), because then f can be moved safely. However, I really can't make it FnOnce because further I find some problems. Fn would work.

However, I really need to capture the result r and return it in consume, like in below

use std::sync::Arc;

pub type OnVirtualTunWrite = Arc<dyn Fn(&dyn Fn(&mut [u8]) , usize) -> Result<(), ()> + Send + Sync>;

struct A {
    on_virtual_tun_write: OnVirtualTunWrite
}

impl A {
    fn consume<R, F>(self, len: usize, f: F) -> Result<R,()>
    where
        F: FnOnce(&mut [u8]) ->  Result<R,()>,
    {
        let mut r: Option<Result<R,()>> = None;
        let result = (self.on_virtual_tun_write)(&|b: &mut [u8]| {
            r = Some(f(b));
        }, len);
        r.unwrap()
    }
}

I know that making it Box<dyn FnOnce(&mut [u8]) would work but I'm trying to avoid dynamic allocation.

Is there a way to make this work?

Cell can be used to change closure to be compatible. F as FnOnce opens runtime panic possibility so maybe better off a Fn.

        use std::cell::Cell;
        let f = {
            let s = Cell::new(Some(f));
            move |b: &mut [u8]| ->  Result<R,()> { s.replace(None).unwrap()(b) }
        };
        
        let r: Cell<Option<Result<R,()>>> = Cell::new(None);
        let result = (self.on_virtual_tun_write)(&|b: &mut [u8]| {
            r.set(Some(f(b)));
        }, len);
        r.take().unwrap()
1 Like

Here's an overly dense version that doesn't panic on multiple calls (r gets set to None instead; could be modified to not change r in that case).

let maybe_f = Cell::new(Some(f));
let r: Cell<Option<Result<R,()>>> = Cell::new(None);
let closure = |b: &mut [u8]| r.set(maybe_f.take().map(|f| f(b)));
let result = (self.on_virtual_tun_write)(&closure, len);
r.take().unwrap()

Cell is not Sync, so if you need that, you may need a different container.

Use &mut dyn FnMut(&mut [u8]):

  pub type OnVirtualTunWrite = Arc<
-     dyn Fn(&    dyn Fn   (&mut [u8]), usize)
+     dyn Fn(&mut dyn FnMut(&mut [u8]), usize)
            -> Result<(), ()>
        + Send + Sync
  >;
…
…
-       let result = (self.on_virtual_tun_write)(&    |b: &mut [u8]| {
+       let result = (self.on_virtual_tun_write)(&mut |b: &mut [u8]| {
            r = Some(f(b));
        }, len);

If you really need the parameter to be an Fn (which does not seem to be the case based on your comment about Box<dyn FnOnce), then indeed you'll need to use Cell

1 Like