gpt4 book ai didi

rust - 如何以闭包作为参数调用闭包

转载 作者:行者123 更新时间:2023-12-03 11:36:09 26 4
gpt4 key购买 nike

我有一个实现特征 A 的结构具有功能fn consume .我想向这个结构传递一个回调,由 fn consume 调用.像这样的东西:pub type OnVirtualTunWrite = Arc<dyn Fn(?, usize) -> Result<(), VirtualTunWriteError> + Send + Sync>;它在 Arc 上因为它在线程之间共享。

struct A {
on_virtual_tun_write: OnVirtualTunWrite
}

impl S for A {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
where
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
{
let mut lower = self.lower.as_ref().borrow_mut();
//I should send this f to self.on_virtual_tun_write
(self.on_virtual_tun_write)(f, len);
//return the appropriate result here
OnVirtualTunWrite是一个应该接收 f,len 的闭包来自 fn consume然后像这样使用它:
let my_on_virtual_tun_write = Arc::new(|?, len| -> ?{
let mut buffer = Vec::new(len);
buffer.resize(len);
//fills buffer with data
f(buffer);
})
我怎样才能使我的 OnVirtualTunWrite ?
我试过 Arc<dyn Fn(dyn FnOnce(&mut [u8]), usize) -> Result<(), ()> + Send + Sync>但它不起作用,因为 dyn Fn必须在编译时知道大小的参数。
另外,还有一个小问题:如何返回 -> smoltcp::Result<R>OnVirtualTunWrite如果 OnVirtualTunWrite不可能知道 R ?

最佳答案

I tried Arc<dyn Fn(dyn FnOnce(&mut [u8]), usize) -> Result<(), ()> + Send + Sync>


那应该是 &dyn FnOnce(...) ,但这也行不通,因为调用 FnOnce自动移动它,因此不能从引用后面调用它。最简单的解决方案是在 consume 中引入额外分配。 , 因为 Box<dyn FnOnce>实现 FnOnce本身 since Rust 1.35 .例如( playground):
pub type OnVirtualTunWrite = Arc<
dyn Fn(Box<dyn FnOnce(&mut [u8])>, usize) -> Result<(), VirtualTunWriteError> + Send + Sync,
>;

pub struct A {
pub on_virtual_tun_write: OnVirtualTunWrite,
}

impl A {
pub fn consume<F>(&self, f: F)
where
F: FnOnce(&mut [u8]) + 'static,
{
(self.on_virtual_tun_write)(Box::new(f), 0).unwrap();
}
}
为避免分配,您可以使用技术 described here调用 FnOnce来自 FnMut .它使用 Option而不是 Box ,所以它是零成本的,或者至少是免费的。例如(完整代码 in the playground ):
pub type OnVirtualTunWrite = Arc<
dyn Fn(&mut dyn FnMut(&mut [u8]), usize) -> Result<(), VirtualTunWriteError> + Send + Sync,
>;

trait CallOnceSafe {
fn call_once_safe(&mut self, x: &mut [u8]);
}

impl<F: FnOnce(&mut [u8])> CallOnceSafe for Option<F> {
fn call_once_safe(&mut self, x: &mut [u8]) {
// panics if called more than once - but A::consume() calls it
// only once
let func = self.take().unwrap();
func(x)
}
}

impl A {
pub fn consume<F>(&self, f: F)
where
F: FnOnce(&mut [u8]) + 'static,
{
let mut f = Some(f);
(self.on_virtual_tun_write)(&mut move |x| f.call_once_safe(x), 0).unwrap();
}
}
上述工作首先移动 FnMut进入 Option , 并通过 call_once_safe 调用它, 一个私有(private)的方法 CallOnceSafe Option<T: FnOnce(...)> 的全面 impl 特征.全面实现将关闭移出 Option并调用它。这是允许的,因为闭包的大小在总括实现中是已知的,它在 T 上是通用的。 .
全面的 impl 可以摆脱变异而不是消耗 self因为它使用 Option::take将内容移出选项,同时将其留空并以其他方式可用。不使用该选项允许 call_once_safeFnMut 调用闭包,例如 consume 中创建的闭包并作为参数传递给 OnVirtualTunWrite . call_once_safe确实消耗了实际的 FnOnce Option 中包含的关闭,从而保持闭包被调用不超过一次的不变量。如果 consume调用 call_once_safe在同一个 Option<F>两次(或者如果外部闭包不止一次调用它的第一个参数),它会导致 panic 。

关于rust - 如何以闭包作为参数调用闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66576915/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com