如何在Rust中克隆Vec<Box<dyn Trait>>并实现排序打印功能
问题解答
现有可运行代码的原理
你当前能正常运行的代码本质上没有拷贝原Vec里的Box<dyn Foo>实例,只是拷贝了元素的不可变引用:
- 输入参数
v是&Vec<Box<dyn Foo>>类型,遍历v时拿到的每个val是&Box<dyn Foo>类型(原Vec中元素的借用) - 编译器自动推导
v_copy的类型是Vec<&Box<dyn Foo>>,只存储引用,不需要获取元素所有权,也不需要对Box或者内部的Bar实例做深拷贝 - 排序只需要调用
value()方法(只需要不可变借用),打印也同理,所以整个流程可以正常运行
疑问1解答
报错原因
你手动标注v_copy类型为Vec<Box<dyn Foo>>后,要求容器存储Box<dyn Foo>的所有权实例,但你循环中push的是&Box<dyn Foo>类型的借用,类型完全不匹配,因此会报错。
和原版本的核心差异
| 版本 | v_copy类型 | 存储内容 | 所有权要求 |
|---|---|---|---|
| 原无标注版本 | Vec<&Box<dyn Foo>> | 原Vec元素的不可变引用 | 不需要获取元素所有权 |
| 手动标注版本 | Vec<Box<dyn Foo>> | Box<dyn Foo>所有权实例 | 需要获得每个元素的所有权(需要克隆原元素) |
修复方案
如果你确实需要克隆Box<dyn Foo>实例存储,需要先给trait Foo添加动态克隆支持,解决trait对象的克隆安全性问题:
- 首先修改
Footrait定义,添加动态克隆方法,同时为Box<dyn Foo>实现Clonetrait:
trait Foo { fn value(&self) -> i32; // 新增动态克隆方法 fn clone_dyn(&self) -> Box<dyn Foo>; } // 为Box<dyn Foo>实现Clone,依赖上面的clone_dyn方法 impl Clone for Box<dyn Foo> { fn clone(&self) -> Self { self.clone_dyn() } }
- 给
Bar结构体添加Clone派生,同时实现clone_dyn方法:
#[derive(Clone)] struct Bar { x: i32, } impl Foo for Bar { fn value(&self) -> i32 { self.x } fn clone_dyn(&self) -> Box<dyn Foo> { Box::new(self.clone()) } }
- 修改
sort_and_print中的push逻辑,克隆元素所有权:
fn sort_and_print(v: &Vec<Box<dyn Foo>>) { let mut v_copy: Vec<Box<dyn Foo>> = Vec::new(); for val in v { // 克隆Box<dyn Foo>获得所有权,存入新Vec v_copy.push(val.clone()); } v_copy.sort_by_key(|o| o.value()); for val in v_copy { println!("{}", val.value()); } }
如果你不需要实际拿到Box的所有权,只是要排序打印,完全没必要用这个方案,直接用存储引用的版本性能更高。
疑问2解答
无法直接v.clone()的原因
v是&Vec<Box<dyn Foo>>类型,调用clone()需要底层Vec<Box<dyn Foo>>实现Clone trait,而Vec实现Clone的前提是元素类型实现了Clone,也就是Box<dyn Foo>实现Clone。默认情况下dyn Foo没有克隆能力,所以无法直接调用。
修复方案
只要完成上面疑问1里的trait克隆适配,让Box<dyn Foo>实现了Clone,就可以直接用clone简化代码,不需要手动循环push:
fn sort_and_print(v: &Vec<Box<dyn Foo>>) { // 直接克隆整个Vec,得到所有权独立的新Vec let mut v_copy = v.clone(); v_copy.sort_by_key(|o| o.value()); for val in v_copy { println!("{}", val.value()); } }
如果不需要持有元素所有权,还有更轻量的简化写法,直接收集引用即可,不需要修改trait定义:
fn sort_and_print(v: &Vec<Box<dyn Foo>>) { // 直接收集原Vec的所有元素引用,和你原手写循环功能完全一致 let mut v_copy: Vec<_> = v.iter().collect(); v_copy.sort_by_key(|o| o.value()); for val in v_copy { println!("{}", val.value()); } }
内容的提问来源于stack exchange,提问作者George Marcus
相关产品推荐
相关产品推荐

