如何克隆存储盒装特征对象的结构?
How to clone a struct storing a boxed trait object?
我编写了一个程序,该程序具有 Animal
特征和实现该特征的结构 Dog
。它还有一个结构 AnimalHouse
将动物存储为特征对象 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();
}
它 returns "Bobby: ruff, ruff!" 正如预期的那样,但是如果我尝试克隆 house
编译器 returns 错误:
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`
我试图在 struct AnimalHouse
之前添加 #[derive(Clone)]
并得到另一个错误:
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`
如何使结构 AnimalHouse
可克隆?一般来说,主动使用特征对象是惯用的 Rust 吗?
有一些问题。首先是没有什么要求 Animal
也实现 Clone
。您可以通过更改特征定义来解决此问题:
trait Animal: Clone {
/* ... */
}
这会导致 Animal
不再是对象安全的,这意味着 Box<dyn Animal>
将变得无效,所以这不是很好。
您可以做的是插入一个额外的步骤。惠特(加上 )。
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();
}
通过引入 clone_box
,我们可以绕过尝试克隆特征对象的问题。
previous answer 正确回答了有关存储盒装特征的问题 object。
关于标题偏离主题,但不是关于使用特征 objects 的惯用方式,替代解决方案可以使用 Rc
智能指针而不是 Box
:这避免了绕过 object 安全的解决方法:
#[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();
}
注意:Rc<T>
仅用于single-threaded场景;还有 Arc<T>
.
我的dyn-clone
crate implements a reusable version of 。有了它,您只需进行最少的更改即可使您的原始代码正常工作。
- 一行添加
DynClone
作为 Animal
的超级特性,要求每个动物实现都是可克隆的。
- 一行为
Box<dyn Animal>
. 生成标准库 Clone
的实现
// [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();
}
我尝试使用 Dk 和 dtolnay 的解决方案,在这种情况下,我需要一个结构,该结构的成员在生成的任务中带有框(通过 tokio)。在那里我收到结构未发送和同步的错误。为了避免这种情况,可以在 Dk 克隆特征中添加发送和同步。也许这也可以添加到 dyn_clone.
我编写了一个程序,该程序具有 Animal
特征和实现该特征的结构 Dog
。它还有一个结构 AnimalHouse
将动物存储为特征对象 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();
}
它 returns "Bobby: ruff, ruff!" 正如预期的那样,但是如果我尝试克隆 house
编译器 returns 错误:
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`
我试图在 struct AnimalHouse
之前添加 #[derive(Clone)]
并得到另一个错误:
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`
如何使结构 AnimalHouse
可克隆?一般来说,主动使用特征对象是惯用的 Rust 吗?
有一些问题。首先是没有什么要求 Animal
也实现 Clone
。您可以通过更改特征定义来解决此问题:
trait Animal: Clone {
/* ... */
}
这会导致 Animal
不再是对象安全的,这意味着 Box<dyn Animal>
将变得无效,所以这不是很好。
您可以做的是插入一个额外的步骤。惠特(加上
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();
}
通过引入 clone_box
,我们可以绕过尝试克隆特征对象的问题。
previous answer 正确回答了有关存储盒装特征的问题 object。
关于标题偏离主题,但不是关于使用特征 objects 的惯用方式,替代解决方案可以使用 Rc
智能指针而不是 Box
:这避免了绕过 object 安全的解决方法:
#[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();
}
注意:Rc<T>
仅用于single-threaded场景;还有 Arc<T>
.
我的dyn-clone
crate implements a reusable version of
- 一行添加
DynClone
作为Animal
的超级特性,要求每个动物实现都是可克隆的。 - 一行为
Box<dyn Animal>
. 生成标准库
Clone
的实现
// [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();
}
我尝试使用 Dk 和 dtolnay 的解决方案,在这种情况下,我需要一个结构,该结构的成员在生成的任务中带有框(通过 tokio)。在那里我收到结构未发送和同步的错误。为了避免这种情况,可以在 Dk 克隆特征中添加发送和同步。也许这也可以添加到 dyn_clone.