rust 可克隆动态闭包的类型?

qoefvg9y  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(164)

我有一个Rust结构体,它包含了许多回调函数。由于结构体的使用方式,我将回调函数建模为动态闭包,如下所示:

struct Callbacks {
    callback_a: Box<dyn Fn() -> i32>,
    callback_b: Box<dyn Fn() -> u64>,
    callback_c: Box<dyn Fn() -> i8>,
}

现在我想为我的结构派生Clone。为了使它工作,每个回调也需要是可克隆的。
我尝试将其表示为Box<dyn (Fn() -> i32) + Clone>,但该语法似乎不是有效的Rust。
如何表示可克隆动态闭包的类型?

fnvucqvd

fnvucqvd1#

语法Box<dyn (Fn() -> i32) + Clone>是有效的Rust语法(尽管那些括号是不必要的-, Box<dyn Fn() -> i32 + Clone>也可以)。但是,现在在dyn trait中有两个trait边界,而Rust不喜欢这样:

error[E0225]: only auto traits can be used as additional traits in a trait object
 --> src/lib.rs:2:41
  |
2 |     callback_a: Box<dyn (Fn() -> i32) + Clone>,
  |                         -------------   ^^^^^ additional non-auto trait
  |                         |
  |                         first non-auto trait
  |
  = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Fn<()> + 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>

就像这条信息所说的,你可以创造新的特质,将想要的特质作为超级特质:

trait CloneableI32Closure: Fn() -> i32 + Clone {}

struct Callbacks {
    callback_a: Box<dyn CloneableI32Closure>,
    // removed other callbacks for simplicity
}

但这也行不通:

error[E0038]: the trait `CloneableI32Closure` cannot be made into an object
 --> src/lib.rs:4:21
  |
4 |     callback_a: Box<dyn CloneableI32Closure>,
  |                     ^^^^^^^^^^^^^^^^^^^^^^^ `CloneableI32Closure` cannot be made into an object
  |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
 --> src/lib.rs:1:42
  |
1 | trait CloneableI32Closure: Fn() -> i32 + Clone {}
  |       -------------------                ^^^^^ ...because it requires `Self: Sized`
  |       |
  |       this trait cannot be made into an object...

Clone不是对象安全的,所以我们的新特征也不是。There are workarounds for this,但是,在这种情况下,使用Rc而不是Box可能更容易:

use std::rc::Rc;

# [derive(Clone)]

struct Callbacks {
    callback_a: Rc<dyn Fn() -> i32>,
    callback_b: Rc<dyn Fn() -> u64>,
    callback_c: Rc<dyn Fn() -> i8>,
}

Playground link
这将有一个轻微的开销来计算引用,但是,动态调度dyn特征的开销可能要高得多--如果性能很关键,泛型结构可能更适合。

相关问题