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 的对比目前超出本指南范围。

最后修改 August 23, 2026: 更新 (499855b16)