Struct 如何克隆存储装箱特征对象的结构?

Struct 如何克隆存储装箱特征对象的结构?,struct,clone,rust,traits,cloneable,Struct,Clone,Rust,Traits,Cloneable,我写了一个程序,它有traitAnimal和structDog来实现这个trait。它还有一个结构AnimalHouse将动物存储为特征对象Box error[E0599]:在当前作用域中找不到类型为“AnimalHouse”的名为“clone”的方法 -->src/main.rs:31:24 | 23 |结构动物屋{ |--------------找不到此方法的“克隆” ... 31 | let house2=house.clone(); | ^^

我写了一个程序,它有trait
Animal
和struct
Dog
来实现这个trait。它还有一个结构
AnimalHouse
将动物存储为特征对象
Box

error[E0599]:在当前作用域中找不到类型为“AnimalHouse”的名为“clone”的方法
-->src/main.rs:31:24
|
23 |结构动物屋{
|--------------找不到此方法的“克隆”
...
31 | let house2=house.clone();
|                        ^^^^^
|
=帮助:只有在trait已实现且在范围内时,才能使用trait中的项
=注意:以下特征定义了一个“克隆”项,您可能需要实现它:
候选人#1:`std::clone::clone`
我试图在
struct AnimalHouse
之前添加
#[派生(克隆)]
,但遇到另一个错误:

error[E0277]:特性绑定的'Animal:std::clone::clone'不满足
-->src/main.rs:25:5
|
25 |动物:盒子,
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^`
|
=注意:由于对`std::boxed::Box`的`std::clone::clone` impl的要求,因此需要此选项`
=注意:std::clone::clone::clone所需`

如何使结构
AnimalHouse
可克隆?一般来说,主动使用trait对象是惯用的方法吗?

存在一些问题。首先,没有必要要求
动物也实现
克隆。您可以通过更改trait定义来解决这个问题:

trait动物:克隆{
/* ... */
}
这将导致
Animal
不再是对象安全的,这意味着
Box
将变得无效,所以这并不好

您可以做的是在whit中插入一个额外的步骤

特征动物:动物克隆{
fn说话(和自我);
}
//将阿尼马克隆分解成它自己的特性,使我们能够提供一个毯子
//实现所有兼容类型,而不必实现
//在本例中,我们对所有具有
//'静态生存期(*即。*它们不包含非'静态指针),以及
//实现动物和克隆。不要问我编译器是如何解析的
//当动物需要动物克隆时,为动物实施动物克隆;I
//不知道为什么会这样。
特性动物克隆{
fn克隆盒(&self)->盒;
}
用于T细胞的impl动物克隆
哪里
T:静态+动物+克隆,
{
fn克隆盒(&self)->盒{
Box::new(self.clone())
}
}
//我们现在可以通过转发到Clone_box手动实现克隆。
盒子的impl克隆{
fn克隆(&self)->框{
self.clone_box()
}
}
#[衍生(克隆)]
结构狗{
名称:String,
}
植入狗{
fn新(名称:&str)->狗{
狗{
name:name.to_string(),
}
}
}
为狗准备的动物{
fn讲话(自我){
println!(“{}:ruff,ruff!”,self.name);
}
}
#[衍生(克隆)]
结构动物屋{
动物:盒子,
}
fn main(){
出租房屋{
动物:盒子:新的(狗:新的(“博比”),
};
let house2=house.clone();
house2.动物。说话();
}
通过引入
clone\u box
,我们可以绕过尝试克隆特征对象的问题。

正确回答了有关存储装箱特征对象的问题

关于标题,而不是关于使用特征对象的惯用方式,另一种解决方案可以是使用
Rc
智能指针而不是
:这避免了绕过对象安全的解决方法:

#[derive(Clone)]
struct AnimalHouse {
    animal: Rc<Animal>,
}

fn main() {
    let house = AnimalHouse { animal: Rc::new(Dog::new("Bobby")) };
    let house2 = house.clone();
    house2.animal.speak();
}
#[派生(克隆)]
结构动物屋{
动物:Rc,
}
fn main(){
let house=AnimalHouse{animal:Rc::new(Dog::new(“Bobby”))};
let house2=house.clone();
house2.动物。说话();
}
注意
Rc
仅用于单线程场景;还有
Arc

我的板条箱实现了的可重用版本。使用它,您可以使您的原始代码只需最少的更改即可工作

  • 一行添加
    DynClone
    作为
    Animal
    的超级特性,要求每个Animal实现都是可克隆的
  • 一行用于为
    生成标准库
克隆
的实现
/[依赖项]
//dyn clone=“1.0”
使用dyn_clone::{clone_trait_object,DynClone};
特征动物:DynClone{
fn说话(和自我);
}
克隆(动物);
#[衍生(克隆)]
结构狗{
名称:String,
}
植入狗{
fn新(名称:&str)->狗{
狗{name:name.to_owned()}
}
}
为狗准备的动物{
fn讲话(自我){
println!{{}:ruff,ruff!”,self.name};
}
}
#[衍生(克隆)]
结构动物屋{
动物:盒子,
}
fn main(){
出租房屋{
动物:盒子:新的(狗:新的(“博比”),
};
let house2=house.clone();
house2.动物。说话();
}

我曾尝试在衍生任务(通过tokio)中需要一个带有box成员的结构时使用Dk和dtolnay的解决方案。在那里,我得到了结构不是发送和同步的错误。为了避免这种情况,可以在Dk clone traits中添加发送和同步。也许这也可以添加到dyn_clone中。

我认为重要的是要注意,如果并且仅当业务逻辑不需要对象的真实克隆,即引用对象的不同副本时,这是一种可行的替代方法e内存中的对象。Rc只是克隆指针,而不是数据本身。我不确定是否应该再问一个问题,但为什么扩展
Clone
意味着你的特征不再是对象安全的?这意味着什么?@Oli
Clone
返回
Self
。这对于特征对象是不允许的,因为无法知道其大小在编译时调用wn。
fn main() {
    let house = AnimalHouse {
        animal: Box::new(Dog::new("Bobby")),
    };
    let house2 = house.clone();
    house2.animal.speak();
}
#[derive(Clone)]
struct AnimalHouse {
    animal: Rc<Animal>,
}

fn main() {
    let house = AnimalHouse { animal: Rc::new(Dog::new("Bobby")) };
    let house2 = house.clone();
    house2.animal.speak();
}
// [dependencies]
// dyn-clone = "1.0"

use dyn_clone::{clone_trait_object, DynClone};

trait Animal: DynClone {
    fn speak(&self);
}

clone_trait_object!(Animal);

#[derive(Clone)]
struct Dog {
    name: String,
}

impl Dog {
    fn new(name: &str) -> Dog {
        Dog { name: name.to_owned() }
    }
}

impl Animal for Dog {
    fn speak(&self) {
        println!{"{}: ruff, ruff!", self.name};
    }
}

#[derive(Clone)]
struct AnimalHouse {
    animal: Box<dyn Animal>,
}

fn main() {
    let house = AnimalHouse {
        animal: Box::new(Dog::new("Bobby")),
    };
    let house2 = house.clone();
    house2.animal.speak();
}