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.
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()
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.
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