4.7.3 可克隆的 Box
译文 · 基于 Learning Rust
可克隆的 Box
原文链接: https://quinedot.github.io/rust-learning/dyn-trait-clone.html
若你希望 Box<dyn Trait> 可以克隆,该怎么办?你不能将 Clone 作为超 trait,因为 Clone 要求 Sized,这会使 Trait 变为 dyn 不兼容。
你可能会想这样做:
1
2
3
| trait Trait {
fn dyn_clone(&self) -> Self where Self: Sized;
}
|
但这样 dyn Trait 上将没有该方法可用,这会成为为 Box<dyn Trait> 实现 Trait 的障碍。
不过,既然这只对实现了 Clone 的基类型才有意义,我们不需要返回 Self 的方法。基类型已有该方法,名为 clone。
我们最终希望得到的是 Box<dyn Trait>,如下:
1
2
3
4
5
6
7
8
9
10
| trait Trait {
fn dyn_clone<'s>(&self) -> Box<dyn Trait + 's> where Self: 's;
}
// 示例实现者
impl Trait for String {
fn dyn_clone<'s>(&self) -> Box<dyn Trait + 's> where Self: 's {
Box::new(self.clone())
}
}
|
若省略所有生命周期相关内容,由于默认 'static 生命周期,它只能与 Self: 'static 配合工作。有时这完全没问题!但本示例我们坚持使用更通用的版本。
该示例实现将使 dyn Trait 做正确的事(通过其实现克隆底层基类型)。我们无法提供默认体,因为实现需要 Clone 和 Sized,而我们不希望这些作为约束。
但这正是我们在手动超 trait 向上转型和先前示例中的 self 接收者辅助 所遇到的情况。同样的模式在这里也适用:将方法移到辅助超 trait,并为适用的情况提供 blanket 实现。
1
2
3
4
5
6
7
8
9
10
11
| trait DynClone {
fn dyn_clone<'s>(&self) -> Box<dyn Trait + 's> where Self: 's;
}
impl<T: Clone + Trait> DynClone for T {
fn dyn_clone<'s>(&self) -> Box<dyn Trait + 's> where Self: 's {
Box::new(self.clone())
}
}
trait Trait: DynClone {}
|
现在我们已为 Box<dyn Trait + '_> 做好准备。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # trait Trait: DynClone {}
# trait DynClone {
# fn dyn_clone<'s>(&self) -> Box<dyn Trait + 's> where Self: 's;
# }
# impl<T: Clone + Trait> DynClone for T {
# fn dyn_clone<'s>(&self) -> Box<dyn Trait + 's> where Self: 's {
# Box::new(self.clone())
# }
# }
impl Trait for Box<dyn Trait + '_> {}
impl Clone for Box<dyn Trait + '_> {
fn clone(&self) -> Self {
// 重要!「递归 trait 实现」风格
(**self).dyn_clone()
}
}
|
重要的是我们调用了 <dyn Trait as DynClone>::dyn_clone!我们对 DynClone 的 blanket 实现约束为 Clone + Trait,但现在我们已为 Box<dyn Trait + '_> 实现了这两者。若我们只调用 self.dyn_clone(),调用图将如下:
1
2
3
4
5
6
7
| <Box<dyn Trait> as Clone >::clone()
<Box<dyn Trait> as DynClone>::dyn_clone()
<Box<dyn Trait> as Clone >::clone()
<Box<dyn Trait> as DynClone>::dyn_clone()
<Box<dyn Trait> as Clone >::clone()
<Box<dyn Trait> as DynClone>::dyn_clone()
...
|
没错,无限递归。与实现 Trait for Box<dyn Trait> 时一样,我们需要直接调用 dyn Trait 的方法以避免此问题。
此用例还有一个 crate:dyn-clone crate。
与 crate 的对比目前超出本指南范围。