我想要一个对特征对象的 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 无法自动推断闭包参数的类型,这使得它对用户来说更加冗长。
您可以通过使用
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()>
(如果他们已经拥有)。