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?
There are a few problems. The first is that there's nothing to require that an
Animal
also implementsClone
. You could fix this by changing the trait definition:This would cause
Animal
to no longer be object safe, meaning thatBox<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).
By introducing
clone_box
, we can get around the problems with attempting to clone a trait object.My
objekt
crate implements a reusable version of DK.'s answer. With it you can make your original code work with a bare minimum of changes.objekt::Clone
as a supertrait ofAnimal
, requiring every animal implementation to be clonable.Clone
forBox<Animal>
.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 aBox
: this avoids the workaround for getting around object safety:Note:
Rc<T>
is only for use in single-threaded scenarios; there's alsoArc<T>
.