我有一个特征和一个实现该特征的结构(一个特征对象)。我想在堆上分配我的特征对象,并让其他结构引用它们。
trait Material {}
struct Iron {}
impl Material for Iron {}
// It works, but takes ownership of boxes.
struct Sphere {
radius: f64,
material: Box<dyn Material>,
}
这段代码有效但我不能有两个共享相同Material
的球体,因为Box
拥有材料而球体拥有其Box
场。
我的下一次尝试是使用普通参考而不是Box
:
struct Sphere<'a> {
radius: f64,
material: &'a dyn Material,
}
这也有效,但据我所知,我的Material
s将被分配到堆栈而不是堆。如果Material
值真的很大并且我宁愿把它放在堆上怎么办?这导致我进入下一个无法编译的方法:
struct Sphere<'a> {
radius: f64,
material: &'a Box<dyn Material>,
}
fn main() {
let m1 = &Box::new(Iron {});
let s1 = Sphere {
radius: 1.0,
material: m1,
};
assert_eq!(s1.radius, 1.0);
}
这给了我以下错误:
error[E0308]: mismatched types
--> src/main.rs:16:19
|
16 | material: m1,
| ^^ expected trait Material, found struct `Iron`
|
= note: expected type `&std::boxed::Box<(dyn Material + 'static)>`
found type `&std::boxed::Box<Iron>`
我不太确定'static
在那种类型中的来源,看起来它混淆了类型检查器。否则dyn Material
和Iron
可以统一到我能理解的程度。
Rc
或Arc
当您需要共享所有权时,Rc
或Arc
通常是第一个可以达到的工具。这些类型通过引用计数实现共享,因此克隆一个是便宜的(只需复制指针并增加引用计数)。在这种情况下,要么轻松工作:
struct Sphere {
radius: f64,
material: Rc<dyn Material>,
}
let m1 = Rc::new(Iron {});
let s1 = Sphere {
radius: 1.0,
material: m1,
};
m1
是混凝土类型Rc<Iron>
,但因为它实现了CoerceUnsized
特性,它可以是automatically coerced在预期Rc<dyn Material>
的背景下。您可以通过Sphere
ing clone
制作多个m1
s参考相同的材料。 (Full example)
Rc
和Arc
之间的区别在于Arc
可以安全地用于多线程之间的共享,但Rc
不是。 (另见When to use Rc vs Box?)
至于你的参考例子:
这也有效,但据我所知,我的材料将被分配到堆栈而不是堆。
确实,生命周期是从堆栈派生的,但引用本身并不需要指向堆栈上的某些东西。例如,您可以通过解除引用T
来引用Box<T>
中的Box
:
struct Sphere<'a> {
radius: f64,
material: &'a dyn Material,
}
let m1 = Box::new(Iron {});
let s1 = Sphere {
radius: 1.0,
material: &*m1, // dereference the `Box` and get a reference to the inside
};
let s2 = Sphere {
radius: 2.0,
material: &*m1,
};
这甚至比使用Rc
便宜,因为&
引用是Copy
able,但即使Iron
本身存储在堆上,指向它的引用仍然绑定到堆栈变量m1
的生命周期。如果你不能让m1
活得足够长,你可能想要使用Rc
而不是普通的引用。
Box
这种方法也应该有效,尽管没有必要。它不是因为,虽然你可以强迫Box<Iron>
到Box<dyn Material>
,你不能强迫&Box<Iron>
到&Box<dyn Material>
;类型是不兼容的。相反,您需要创建一个Box<dyn Material>
类型的堆栈变量,以便您可以引用它。
let m1: Box<dyn Material> = Box::new(Iron {}); // coercion happens here
let s1 = Sphere {
radius: 1.0,
material: &m1, // so that this reference is the right type
};