72

I wrote a program that has the trait Animal and the struct Dog implementing the trait. It also has a struct AnimalHouse storing an animal as a trait object 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();
}

It returns "Bobby: ruff, ruff!" as expected, but if I try to clone house the compiler returns errors:

fn main() {
    let house = AnimalHouse {
        animal: Box::new(Dog::new("Bobby")),
    };
    let house2 = house.clone();
    house2.animal.speak();
}
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`

I tried to add #[derive(Clone)] before struct AnimalHouse and got another error:

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`

How do I make the struct AnimalHouse cloneable? Is it idiomatic Rust to use a trait object actively, in general?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Denis Kreshikhin
  • 8,856
  • 9
  • 52
  • 84

3 Answers3

92

There are a few problems. The first is that there's nothing to require that an Animal also implements Clone. You could fix this by changing the trait definition:

trait Animal: Clone {
    /* ... */
}

This would cause Animal to no longer be object safe, meaning that Box<dyn Animal> will become invalid, so that's not great.

What you can do is insert an additional step. To whit (with additions from @ChrisMorgan's comment).

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 dyn Animal when Animal requires AnimalClone;
// I have *no* idea why this works.
trait AnimalClone {
    fn clone_box(&self) -> Box<dyn Animal>;
}

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

// We can now implement Clone manually by forwarding to clone_box.
impl Clone for Box<dyn Animal> {
    fn clone(&self) -> Box<dyn 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<dyn Animal>,
}

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

By introducing clone_box, we can get around the problems with attempting to clone a trait object.

trent
  • 25,033
  • 7
  • 51
  • 90
DK.
  • 55,277
  • 5
  • 189
  • 162
  • 6
    Not sure if I should make another question or not but why does extending `Clone` mean that your trait is no longer object safe? What does that mean? – Oli Mar 04 '19 at 23:19
  • 8
    @Oli `clone` returns `Self`. This is not allowed for a trait object because the size cannot be known at compile-time. – Peter Hall Jun 19 '19 at 13:28
  • 1
    Stupid question: How does one implement this for types with limited lifetime (non-'static)? – soulsource Dec 04 '21 at 10:36
  • 1
    @soulsource: I was struggling with this too. After much research, it turns out the 'static lifetime is actually implicit for all owned types. Box is always equivalent to Box, since Box always owns its contents. Lifetimes only apply to references in rust. https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html – ilcj Oct 30 '22 at 21:46
  • @PeterHall how does vec succeed to implement clone while remaining sized? – theonlygusti Feb 13 '23 at 12:42
  • @theonlygusti I'm afraid I don't understand your question. – Peter Hall Feb 14 '23 at 01:01
37

My dyn-clone crate implements a reusable version of DK.'s answer. With it you can make your original code work with a bare minimum of changes.

  • One line to add DynClone as a supertrait of Animal, requiring every animal implementation to be clonable.
  • One line to generate an implementation of the standard library Clone for Box<dyn Animal>.

// [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();
}
dtolnay
  • 9,621
  • 5
  • 41
  • 62
18

The previous answer correctly answers the question about storing a boxed trait object.

Getting off topic with respect to the title, but not about the idiomatic way of using trait objects, an alternative solution could be use the Rc smart pointer instead of a Box: this avoids the workaround for getting around object safety:

#[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();
}

Note: Rc<T> is only for use in single-threaded scenarios; there's also Arc<T>.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
attdona
  • 17,196
  • 7
  • 49
  • 60
  • 16
    I think it's important to note that this is a viable alternative *if and only if* the business logic doesn't need a true clone of the object, i.e. to reference a distinct copy of the object in memory. Rc merely clones the pointer, not the data itself. – jonny Apr 25 '18 at 11:53
  • 2
    Also worth noting that this is not acceptable if the data is ever borrowed as mutable, since you can't borrow an Rc as mutable. – Eric Smith Oct 03 '21 at 23:34
  • Why does it not need `dyn Animal`? – theonlygusti Feb 13 '23 at 12:46
  • I still get: the trait `X` cannot be made into an object – theonlygusti Feb 13 '23 at 13:01