如何克隆存储盒装特征对象的结构? [英] How to clone a struct storing a boxed trait object?
问题描述
我写了一个程序,该程序具有特征 Animal
和实现该特征的结构 Dog
。它也有一个结构 AnimalHouse
,将动物存储为特征对象 Box< Animal>
。
性状动物{
fn talk(&self; self ;;
}
struct Dog {
name:String,
}
impl Dog {
fn new(name:& ; str)-> Dog {
return Dog {
name:name.to_string(),
};
}
}
impl狗的动物{
fn说话(和自我){
println!{ {}::! ,self.name};
}
}
struct AnimalHouse {
animal:Box< Animal> ;,
}
fn main(){
let house = AnimalHouse {
动物:Box :: new(Dog :: new( Bobby)),
};
house.animal.speak();
}
返回 Bobby:ruff,ruff!如预期的那样,但是如果我尝试克隆 house
,编译器将返回错误:
fn main(){
let house = AnimalHouse {
animal:Box :: new(Dog :: new( Bobby)),
};
let house2 = house.clone();
house2.animal.speak();
}
错误[E0599]:在当前范围
->中找不到类型为 AnimalHouse的名为 clone的方法; src / main.rs:31:24
|
23 | struct AnimalHouse {
| ------------------未为此
...
31找到方法`clone`。让house2 = house.clone();
| ^^^^^
|
=帮助:特征中的项目只有在实现了特征并且在范围内时才可以使用
=注意:以下特征定义了一个项目克隆,也许您需要实现它:
候选人#1:`std :: clone :: Clone`
我尝试添加#[derive(Clone)]
在 struct AnimalHouse
之前,出现另一个错误:
< pre $ = lang-none prettyprint-override>
error [E0277]:限制了动物的特性:std :: clone :: Clone`
-> src / main.rs:25:5
|
25 |动物:Box< Animal>,
| ^^^^^^^^^^^^^^^^^^^^^的特性`std :: clone :: Clone`没有为ʻAnimal`实现。
=注意:必需,因为对std :: boxed :: Box< Animal>的`std :: clone :: Clone`的隐含要求
=注意:`std:必需: :clone :: Clone :: clone`
如何制作结构 AnimalHouse
是否可克隆?通常,通常主动使用特征对象是Rust惯用的吗?
存在一些问题。首先,没有什么要求 Animal
也要实现 Clone
。您可以通过更改特征定义来解决此问题:
特征动物:克隆{
/ * ... * /
}
这会导致动物
不再是对象安全的,这意味着 Box< Animal>
将变得无效,所以这不是很好。
您可以要做的是插入一个附加步骤。投身(加上 @ChrisMorgan的评论
性状动物:AnimalClone {
fn talk(& self);
}
//将AnimalClone拆分为自己的特征使我们能够为所有兼容类型提供全面的
//实现,而不必实现
//动物的其余部分。在这种情况下,我们为所有具有
//静态寿命的类型(*即它们不包含非静态指针)实现它,并且
//实现Animal和Clone。不要问我,当Animal需要AnimalClone时,编译器如何解析
//为Animal实现AnimalClone;我
//不知道为什么会这样。
性状AnimalClone {
fn clone_box(& self)-> Box< Animal>
}
impl< T> T的动物克隆
,其中
T:静态+动物+克隆,
{
fn clone_box(& self)-> Box< Animal> {
Box :: new(self.clone())
}
}
//我们现在可以通过转发到clone_box来手动实现Clone。
impl为Box< Animal>复制{
fn clone(& self)-> Box< Animal> {
self.clone_box()
}
}
#[derive(Clone)]
struct Dog {
name:String,
}
impl Dog {
fn new(name:& str)->狗{
狗{
名称:name.to_string(),
}
}
}
impl狗的动物{
fn talk(& 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();
}
通过引入 clone_box
,我们可以尝试克隆特征对象来解决这些问题。
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<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 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();
}
By introducing clone_box
, we can get around the problems with attempting to clone a trait object.
这篇关于如何克隆存储盒装特征对象的结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!