rust - 如何以闭包作为参数调用闭包
问题描述
我有一个结构,它实现了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
fromfn 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
解决方案
我试过了
Arc<dyn Fn(dyn FnOnce(&mut [u8]), usize) -> Result<(), ()> + Send + Sync>
应该是&dyn FnOnce(...)
,但这也不起作用,因为调用会FnOnce
自动移动它,所以不能从引用后面调用它。最简单的解决方案是在 中引入额外的分配consume
,因为从 Rust 1.35 开始Box<dyn FnOnce>
实现FnOnce
自身。例如(游乐场):
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();
}
}
为避免分配,您可以使用此处描述的技术FnOnce
从FnMut
. 它使用Option
而不是Box
,所以它是零成本,或者至少是免费的。例如(操场上的完整代码):
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
到一个中,并通过一个私有trait上的方法Option
调用它,该方法带有一个毯子 impl for 。一揽子实现将闭包移出并调用它。这是允许的,因为闭包的大小在毯子实现中是已知的,这是通用的 over 。call_once_safe
CallOnceSafe
Option<T: FnOnce(...)>
Option
T
毯子 impl 可以摆脱变异而不是消耗self
,因为它用于Option::take
将内容移出选项,同时将其留空并以其他方式可用。不使用该选项允许call_once_safe
从FnMut
闭包中调用,例如在闭包中创建consume
并作为参数传递给OnVirtualTunWrite
. call_once_safe
确实消耗了FnOnce
包含在 中的实际闭包Option
,因此保留了闭包被调用不超过一次的不变量。如果consume
要调用call_once_safe
相同的Option<F>
两次(或者如果外部闭包多次调用其第一个参数),则会导致恐慌。
推荐阅读
- python - 如何在子进程python期间显示进度条
- laravel - SQLSTATE [23000]:违反完整性约束:1052 where 子句中的列 'id' 不明确
- c++ - Tensorflow C API 为 RGB 图像返回错误的结果
- mesh - gmsh 网格可以在不使用超限选项的情况下更加结构化吗?
- c# - 访问 JsonApiDotNetCore 中 IResourceService 中的字段过滤器参数
- javascript - 获取目录中的所有图像,在图像上键入每个图像的文件名
- powershell - Active Directory 委派,特定属性
- python - 如何在数字后添加换行符?
- javascript - 为什么 JavaScript 的 dispatchEvent 在 IOS 移动应用上不起作用?
- reactjs - react-select 可以显示 2 列项目而不是 1 列吗?