snippetrustCritical
How to clone a struct storing a boxed trait object?
Viewed 0 times
objecthowstructboxedclonestoringtrait
Problem
I wrote a program that has the trait
It returns "Bobby: ruff, ruff!" as expected, but if I try to clone
I tried to add
How do I make the struct
Animal and the struct Dog implementing the trait. It also has a struct AnimalHouse storing an animal as a trait object Box.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,
}
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,
| ^^^^^^^^^^^^^^^^^^^ 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
= 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?Solution
There are a few problems. The first is that there's nothing to require that an
This would cause
What you can do is insert an additional step. To whit (with additions from @ChrisMorgan's comment).
By introducing
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 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;
}
impl AnimalClone for T
where
T: 'static + Animal + Clone,
{
fn clone_box(&self) -> Box {
Box::new(self.clone())
}
}
// We can now implement Clone manually by forwarding to clone_box.
impl Clone for Box {
fn clone(&self) -> Box {
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,
}
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.Context
Stack Overflow Q#30353462, score: 108
Revisions (0)
No revisions yet.