Rust - 在内存布局和更简单的泛型的枚举中包装特征?
Rust - wrapping trait in an enum for memory layout and simpler generics?
所以我的代码中有如下内容:
// Trait
trait Shape {
fn area(&self) -> f32;
}
// Rect
struct Rect {
width: f32,
height: f32
}
impl Shape for Rect {
fn area(&self) -> f32 {
self.width * self.height
}
}
// Circle
struct Circle {
radius: f32
}
impl Shape for Circle {
fn area(&self) -> f32 {
self.radius * self.radius * std::f32::consts::PI
}
}
// usage
fn use_shapes(shapes: Vec<Box<dyn Shape>>) {
// ...
}
而且我真的不喜欢 Box<dyn ...>
,无论是性能还是感觉很恶心。我对我的特质的实现很少而且很明确,所以我觉得成为枚举是一个很好的候选人。
在将其转换为枚举的过程中,我偶然发现了以下模式:
// Wrapper enum
enum ShapeEnum {
Rect(Rect),
Circle(Circle)
}
impl Shape for ShapeEnum {
fn area(&self) -> f32 {
match self {
ShapeEnum::Rect(data) => data.area(),
ShapeEnum::Circle(data) => data.area(),
}
}
}
// new usage
fn use_shapes(shapes: Vec<ShapeEnum>) {
// ...
}
非常整洁。它也感觉像是在某种程度上作弊。它按预期编译和工作,它非常不寻常,我想看看是否有任何我现在没有看到的意外 drawbacks/costs/quirks?
我还想知道,由于枚举实现的确定性,它是否会成为一个好的宏?自动围绕特征及其一组实现器生成枚举,它本身实现了特征,就像 dyn
版本一样。
I wanted to see if there are any unexpected drawbacks/costs/quirks that I'm not seeing right now?
我能想到的唯一真正的缺点是你最终集中了所有这些类型的定义——这使得第三方更难挂钩到你的代码中。
您可以通过添加动态分派枚举来解决这个问题,这意味着您只会在那些外部定义的类型上获得较慢的行为。
// Wrapper enum
enum ShapeEnum {
Rect(Rect),
Circle(Circle),
Dynamic(Box<T: Shape>),
}
impl Shape for ShapeEnum {
fn area(&self) -> f32 {
match self {
ShapeEnum::Rect(data) => data.area(),
ShapeEnum::Circle(data) => data.area(),
ShapeEnum::Dynamic::(data) => data.area(),
}
}
}
I'm also wondering if, because of the deterministic nature of the enum
implementation, it would make a good macro? Automatically generating
an enum around a trait and a set of its implementors, which itself
implements the trait just like a dyn version would
看起来 enum_dispatch 板条箱几乎完全可以做到这一点。
免责声明:我自己没用过。
所以我的代码中有如下内容:
// Trait
trait Shape {
fn area(&self) -> f32;
}
// Rect
struct Rect {
width: f32,
height: f32
}
impl Shape for Rect {
fn area(&self) -> f32 {
self.width * self.height
}
}
// Circle
struct Circle {
radius: f32
}
impl Shape for Circle {
fn area(&self) -> f32 {
self.radius * self.radius * std::f32::consts::PI
}
}
// usage
fn use_shapes(shapes: Vec<Box<dyn Shape>>) {
// ...
}
而且我真的不喜欢 Box<dyn ...>
,无论是性能还是感觉很恶心。我对我的特质的实现很少而且很明确,所以我觉得成为枚举是一个很好的候选人。
在将其转换为枚举的过程中,我偶然发现了以下模式:
// Wrapper enum
enum ShapeEnum {
Rect(Rect),
Circle(Circle)
}
impl Shape for ShapeEnum {
fn area(&self) -> f32 {
match self {
ShapeEnum::Rect(data) => data.area(),
ShapeEnum::Circle(data) => data.area(),
}
}
}
// new usage
fn use_shapes(shapes: Vec<ShapeEnum>) {
// ...
}
非常整洁。它也感觉像是在某种程度上作弊。它按预期编译和工作,它非常不寻常,我想看看是否有任何我现在没有看到的意外 drawbacks/costs/quirks?
我还想知道,由于枚举实现的确定性,它是否会成为一个好的宏?自动围绕特征及其一组实现器生成枚举,它本身实现了特征,就像 dyn
版本一样。
I wanted to see if there are any unexpected drawbacks/costs/quirks that I'm not seeing right now?
我能想到的唯一真正的缺点是你最终集中了所有这些类型的定义——这使得第三方更难挂钩到你的代码中。
您可以通过添加动态分派枚举来解决这个问题,这意味着您只会在那些外部定义的类型上获得较慢的行为。
// Wrapper enum
enum ShapeEnum {
Rect(Rect),
Circle(Circle),
Dynamic(Box<T: Shape>),
}
impl Shape for ShapeEnum {
fn area(&self) -> f32 {
match self {
ShapeEnum::Rect(data) => data.area(),
ShapeEnum::Circle(data) => data.area(),
ShapeEnum::Dynamic::(data) => data.area(),
}
}
}
I'm also wondering if, because of the deterministic nature of the enum implementation, it would make a good macro? Automatically generating an enum around a trait and a set of its implementors, which itself implements the trait just like a dyn version would
看起来 enum_dispatch 板条箱几乎完全可以做到这一点。
免责声明:我自己没用过。