0

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?

Gatonito
  • 1,662
  • 5
  • 26
  • 55
  • 1
    Doesn't the second part of [this answer](https://stackoverflow.com/a/66579120/1600898) to your previous (and similar) question cover this? – user4815162342 Mar 24 '21 at 20:34

1 Answers1

1

There are two changes needed to make this compile. One is to change F: FnOnce(&mut [u8])... in the consume() function definition to F: Fn(&mut [u8])... An FnOnce()-type closure will consume itself when called, so it can only be called once.

Changing this will give another error that r cannot be assigned to inside the closure. This is because r is a capture variable from outside the closure, and the Fn() type will borrow captured variables immutably. By changing the second Fn() to FnMut(), the r variable will be captured as a mutable reference, and can therefore be assigned to from inside the closure:

Arc<dyn Fn(&dyn FnMut(&mut [u8]) , usize) -> Result<(), ()> + Send + Sync>
transistor
  • 1,480
  • 1
  • 9
  • 12
  • unfortunately I cannot edit the `consume` function – Gatonito Mar 24 '21 at 23:55
  • If you can't change the consume function, what are you able to change? Since the consume function will consume the struct A object passed to it as self, the closure in `on_virtual_tun_write` can only be called once, so using FnOnce(&mut [u8]) should be ok given .cosume(). The issue here must lie in how you're using this function, so we'd need to know more about what you're trying to do one level up. You will probably need to clone the struct object before calling consume, and also clone the FnOnce() closure passed to it as f, or it might be better to do things very differently, depending – transistor Mar 25 '21 at 02:49
  • I can change `OnVirtualTunWrite` entirely, but I'm avoiding dynamic allocation so I cannot use `Box` on its arguments – Gatonito Mar 25 '21 at 03:24
  • We need more information about what you're trying to achieve in order to help you. Can you give the code that creates and calls the A struct and consume function, and tell us a bit about what you're trying to achieve with the code? – transistor Mar 25 '21 at 15:49