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

Den*_*hin 20 struct clone traits cloneable rust

我编写了一个程序,它具有特征AnimalDog实现特征的结构.它还有一个AnimalHouse存储动物作为特征对象的结构Box<Animal>.

trait Animal {
    fn speak(&self);
}

struct Dog {
    name: String,
}

impl Dog {
    fn new(name: &str) -> Dog {
        return Dog {
            name: name.to_string(),
        };
    }
}

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

struct AnimalHouse {
    animal: Box<Animal>,
}

fn main() {
    let house = AnimalHouse {
        animal: Box::new(Dog::new("Bobby")),
    };
    house.animal.speak();
}
Run Code Online (Sandbox Code Playgroud)

它返回"Bobby:ruff,ruff!" 正如所料,但如果我尝试克隆house编译器返回错误:

fn main() {
    let house = AnimalHouse {
        animal: Box::new(Dog::new("Bobby")),
    };
    let house2 = house.clone();
    house2.animal.speak();
}
Run Code Online (Sandbox Code Playgroud)
error[E0599]: no method named `clone` found for type `AnimalHouse` in the current scope
  --> src/main.rs:31:24
   |
23 | struct AnimalHouse {
   | ------------------ method `clone` not found for this
...
31 |     let house2 = house.clone();
   |                        ^^^^^
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `clone`, perhaps you need to implement it:
           candidate #1: `std::clone::Clone`
Run Code Online (Sandbox Code Playgroud)

#[derive(Clone)]之前试图添加struct AnimalHouse并得到另一个错误:

error[E0277]: the trait bound `Animal: std::clone::Clone` is not satisfied
  --> src/main.rs:25:5
   |
25 |     animal: Box<Animal>,
   |     ^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `Animal`
   |
   = note: required because of the requirements on the impl of `std::clone::Clone` for `std::boxed::Box<Animal>`
   = note: required by `std::clone::Clone::clone`
Run Code Online (Sandbox Code Playgroud)

如何使结构可AnimalHouse克隆?一般来说,积极使用特质对象是惯用的吗?

DK.*_*DK. 23

有一些问题.首先,没有什么要求Animal也可以实现Clone.您可以通过更改特征定义来解决此问题:

trait Animal: Clone {
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

这将导致Animal不再是对象安全,这意味着Box<Animal>它将变得无效,因此不是很好.

可以做的是插入一个额外的步骤.要惠特(加上@ChrisMorgan的评论).

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

// Splitting AnimalClone into its own trait allows us to provide a blanket
// implementation for all compatible types, without having to implement the
// rest of Animal.  In this case, we implement it for all types that have
// 'static lifetime (*i.e.* they don't contain non-'static pointers), and
// implement both Animal and Clone.  Don't ask me how the compiler resolves
// implementing AnimalClone for Animal when Animal requires AnimalClone; I
// have *no* idea why this works.
trait AnimalClone {
    fn clone_box(&self) -> Box<Animal>;
}

impl<T> AnimalClone for T
where
    T: 'static + Animal + Clone,
{
    fn clone_box(&self) -> Box<Animal> {
        Box::new(self.clone())
    }
}

// We can now implement Clone manually by forwarding to clone_box.
impl Clone for Box<Animal> {
    fn clone(&self) -> Box<Animal> {
        self.clone_box()
    }
}

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

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

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

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

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

通过介绍clone_box,我们可以解决尝试克隆特征对象的问题.

  • 不确定我是否应该再问一个问题,但为什么扩展 `Clone` 意味着你的 trait 不再是对象安全的?这意味着什么? (3认同)
  • @Oli `clone` 返回 `Self`。这对于 trait 对象是不允许的,因为在编译时无法知道大小。 (3认同)
  • @soulsource:我也在为此苦苦挣扎。经过大量研究,事实证明“静态生命周期实际上对于所有拥有的类型都是隐式的”。Box&lt;T&gt; 始终等价于 Box&lt;T + 'static&gt;,因为 Box 始终拥有其内容。生命周期仅适用于 Rust 中的引用。https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html (2认同)

dto*_*nay 8

我的objekt箱子实现的可重复使用的版本DK。的回答。有了它,您只需很少的更改就可以使原始代码正常工作。

  • 一行导入板条箱。
  • 要添加的一行objekt::Clone作为的特征Animal,要求每个动物实现都是可克隆的。
  • 一行为生成标准库的Clone实现Box<Animal>

#[macro_use] extern crate objekt;

trait Animal: objekt::Clone {
    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<Animal>,
}

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


att*_*ona 5

以前的答案正确地回答了关于存储盒装特质对象的问题。

关于标题,而不是使用特质对象的惯用方式,可以采用另一种解决方案,即使用Rc智能指针代替a Box:这避免了解决对象安全性的变通方法:

#[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();
}
Run Code Online (Sandbox Code Playgroud)

注意Rc<T>仅用于单线程方案;还有Arc<T>

  • 我认为必须注意,这是一个可行的选择,当且仅当业务逻辑不需要对象的真实克隆,即引用内存中对象的不同副本时,这才是可行的。Rc仅克隆指针,而不克隆数据本身。 (6认同)
  • 另外值得注意的是,如果数据曾经作为可变借用,这是不可接受的,因为您不能借用 Rc 作为可变数据。 (2认同)