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 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.