如何共享堆分配的特征对象?
How to share heap-allocated trait objects?
我有一个特征和一个实现该特征的结构(一个特征对象)。我想在堆上分配我的特征对象并让其他结构引用它们。
方框字段
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
拥有 material 而一个球体拥有其 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
trait, it can be in contexts that expect an Rc<dyn Material>
. You can make multiple Sphere
s refer to the same material by clone
ing m1
. (Full example)
Rc
和 Arc
的区别在于 Arc
可以安全地用于多个线程之间的共享,但 Rc
则不然。 (另见 )
参考资料
至于你的参考例子:
This also works, but as far as I understand, my Materials will be allocated on the stack instead of the heap.
确实生命周期是从栈中派生出来的,但是引用本身不需要指向栈上的东西。例如,您可以通过取消引用 Box
:
来引用 Box<T>
中的 T
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
可用的,但是即使 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
};
我有一个特征和一个实现该特征的结构(一个特征对象)。我想在堆上分配我的特征对象并让其他结构引用它们。
方框字段
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
拥有 material 而一个球体拥有其 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
trait, it can be Rc<dyn Material>
. You can make multiple Sphere
s refer to the same material by clone
ing m1
. (Full example)
Rc
和 Arc
的区别在于 Arc
可以安全地用于多个线程之间的共享,但 Rc
则不然。 (另见
参考资料
至于你的参考例子:
This also works, but as far as I understand, my Materials will be allocated on the stack instead of the heap.
确实生命周期是从栈中派生出来的,但是引用本身不需要指向栈上的东西。例如,您可以通过取消引用 Box
:
Box<T>
中的 T
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
可用的,但是即使 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
};