如何将闭包传递给 dyn Trait 对象

问题描述 投票:0回答:1

我想要一个对特征对象的 dyn 引用,我在该对象上有一个方法,以闭包作为参数:

trait DynTrait {
    fn dyn_method(&mut self, closure: impl FnMut(&str) + 'static);
}

// ...

let dyn_object: &mut dyn DynTrait = ...;

闭包的生命周期可以比对

dyn_method
的调用更长,因此它将被存储为某种形式的
Box
/
Rc
/... .

此示例编译时不会出现错误:

error[E0038]: the trait `DynTrait` cannot be made into an object

这是有道理的,因为

impl
只是泛型函数的语法糖,而具有泛型函数的特征不能被制作成对象。

解决此问题的一种方法是将

impl
替换为
&dyn
(用户现在必须指定
&mut
):

trait DynTrait {
    fn dyn_method(&mut self, closure: &mut (dyn FnMut(&str) + 'static));
}

但在这种情况下,我们没有获得闭包的所有权,也无法将其变成

Box
/
Rc
/... 。我们可以制作
&dyn
ref
'static
,但这违背了闭包的全部目的,我们可以只使用函数指针。

另一种方式是直接传入一个

Box
/
Rc
/...:

trait DynTrait {
    fn dyn_method(&mut self, closure: Box<dyn FnMut(&str) + 'static>);
}

这工作得很好,但现在实现细节泄露给了用户,让他变得更加冗长,因为他总是必须将闭包传递给

Box::new()
。此外,有时 Rust 无法自动推断闭包参数的类型,这使得它对用户来说更加冗长。

游乐场

rust callback closures vtable dynamic-dispatch
1个回答
0
投票

您可以通过使用

Box<dyn Fn()>
定义上一个示例中的特征来解决此问题,然后将使用原始语法实现该方法的扩展特征添加到实现
DynTrait
的所有内容中:

trait DynTrait {
    fn dyn_method_boxed(&mut self, closure: Box<dyn FnMut(&str) + 'static>);
}

trait DynTraitExt {
    fn dyn_method(&mut self, closure: impl FnMut(&str) + 'static);
}

impl<T: ?Sized + DynTrait> DynTraitExt for T {
    fn dyn_method(&mut self, closure: impl FnMut(&str) + 'static) {
        self.dyn_method_boxed(Box::new(closure))
    }
}

我已经重命名了采用

Box
的方法,否则它的名称会与扩展特征中的 on 发生冲突。

这种方法还可以让用户直接传递他们的

Box<dyn Fn()>
(如果他们已经拥有)。

游乐场

© www.soinside.com 2019 - 2024. All rights reserved.