rust - 如何创建实现 Fn 并可以克隆到不同对象的特征对象?
问题描述
如果我想要一个不可复制的类型擦除(动态类型)可调用,那就是
Box<dyn Fn(i32) -> ()>
如果我想要一个引用计数的类型擦除可调用,那就是(取决于我是否需要线程安全)
Rc<dyn Fn(i32) -> ()>
Arc<dyn Fn(i32) -> ()>
但是在这里,所有副本都引用相同的底层内存——它们并没有区别。
如果我想要不同的可调用对象,我该怎么做?在 implements时Box<T>
已经实现,但没有实现,所以这里不适用。做类似的事情:Clone
T
Clone
Fn
Clone
Box<dyn Fn(i32) -> () + Clone>
失败:
error[E0225]: only auto traits can be used as additional traits in a trait object
--> src/main.rs:7:35
|
7 | fn foo(f: Box<dyn Fn(i32) -> () + Clone>) {
| ------------- ^^^^^ additional non-auto trait
| |
| first non-auto trait
|
= help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Fn<(i32,)> + Clone {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
由于 的拼写,错误中的建议不起作用Fn
,但是:
trait CopyableFn: Fn(i32) -> () + Clone {}
Box<dyn CopyableFn>
本身也不起作用,因为:
error[E0038]: the trait `CopyableFn` cannot be made into an object
--> src/main.rs:7:11
|
5 | trait CopyableFn: Fn(i32) -> () + Clone {}
| ---------- ----- ...because it requires `Self: Sized`
| |
| this trait cannot be made into an object...
6 |
7 | fn foo(f: Box<dyn CopyableFn>) {
| ^^^^^^^^^^^^^^^^^^^ the trait `CopyableFn` cannot be made into an object
有没有一种方法可以创建一个Fn
可克隆的类型对象,以便副本是不同的?
解决方案
与其CloneableFn
成为 的超特征Clone
,不如实现一个clone_box
将其克隆到盒子中的方法:
trait CloneableFn: Fn(i32) -> () {
fn clone_box<'a>(&self) -> Box<dyn 'a + CloneableFn>
where
Self: 'a;
}
由于dyn CloneableFn
无法克隆(Clone
requires Sized
)之类的未调整大小的类型,因此这里没有理由将其Clone
作为超特征。但是,拥有Fn(i32) -> ()
作为超特征可以正常调用函数。
然后CloneableFn
可以为所有同时实现Fn(i32) -> ()
和的类型实现Clone
:
impl<F> CloneableFn for F
where
F: Fn(i32) -> () + Clone,
{
fn clone_box<'a>(&self) -> Box<dyn 'a + CloneableFn>
where
Self: 'a,
{
Box::new(self.clone())
}
}
最后,Box<dyn CloneableFn>
不会自动实现,Clone
因为dyn CloneableFn
不会,所以我们可以自己实现:
impl<'a> Clone for Box<dyn 'a + CloneableFn> {
fn clone(&self) -> Self {
(**self).clone()
}
}
有了这个,您现在可以克隆Box<dyn CloneableFn>
并将其作为常规函数调用:
// let closure borrow some shared state
use std::sync::atomic::{AtomicI32, Ordering};
let x = AtomicI32::new(0);
let f = |n| {
println!("x = {}", x.fetch_add(n, Ordering::Relaxed));
};
let f: Box<dyn CloneableFn> = Box::new(f);
let g = f.clone();
f(3);
g(5);
f(7);
这与如何克隆存储盒装特征对象的结构有关?,但在这种情况下,目标特征 ( Animal
) 可以更改为具有超特征,而在这种情况下,这是不可能的(因为目标特征是Fn(i32) -> ()
)。在某种程度上,这是相反的方法:添加目标是超特征的特征,而不是向目标添加超特征。
推荐阅读
- php - Laravel 5.6 中的 Cron 作业,用于发送带有生成的 PDF 文件的电子邮件
- python - 如何在word文档中提取Section的正文?Python
- conv-neural-network - 哪张照片属于较热的一天?
- javascript - TypeError:无法读取未定义 json react-js 的属性“0”
- git - 您可以将本地 git 存储库保存在与工作目录不同的驱动器上吗?
- javascript - 在类似于继承树的代码中处理回调
- python - 如何在 python 中进行聊天
- hibernate - 用于限制实体中的集合/列表的休眠注释
- jquery - 递增运行脚本
- android - 难以显示动态添加的表行