鉴于这两个结构:
pub struct RectangleRenderer {
canvas: Canvas,
origin: Point,
shape: Rectangle,
}
pub struct CircleRenderer {
canvas: Canvas,
origin: Point,
shape: Circle,
}
由于我来自 Java,我会从中提取一个基类
ShapeRenderer
并将字段 canvas
和 origin
应用到其中,而特定类型将保留其名为 shape
的字段。对于这种情况,Rust 的最佳实践是什么,因为特征仅与接口类似,因此不允许属性/字段?
这看起来是泛型的完美案例。
您可以像这样创建一个结构:
struct ShapeRenderer<T: Shape> {
canvas: Canvas,
origin: Point,
shape: T,
}
请注意,我已通过特征
T
(您必须创建)来限制泛型类型 Shape
。您可以在此处设置您喜欢的任何限制(或根本没有限制),但您将仅限于使用这些特征的成员。
您希望能够在形状中访问的任何内容都需要通过
Shape
来暴露。例如,如果您需要中心和区域,那么特征定义可能如下所示:
trait Shape {
fn center(&self) -> (f64, f64);
fn area(&self) -> f64;
}
如果这还不够灵活,您还可以仅针对特定形状提供
ShapeRenderer
特殊行为。例如:
impl ShapeRenderer<Rectangle> {
fn n_sides(&self) -> u32 {
4
}
}
请注意,在这个
impl
中,我们可以访问 Rectangle
的所有字段,而不仅仅是 Shape
中的函数。
或者,您可以创建一个基本结构,然后将其包含为最终结构的成员:
struct Renderer {
canvas: Canvas,
origin: Point,
}
struct CircleRenderer {
renderer: Renderer,
shape: Circle,
}
struct RectangleRenderer {
renderer: Renderer,
shape: Rectangle,
}
这是 Rust 中最接近标准继承的东西。
第三,如果您在创建这些结构时只关心代码重复,并且不希望它们共享除字段之外的任何内容,则可以使用宏:
macro_rules! make_renderer {
($name: ty, $shape: ty) => (
struct $name {
canvas: Canvas,
origin: Point,
shape: $shape,
}
);
}
make_renderer!(CircleRenderer, Circle);
make_renderer!(RectangleRenderer, Rectangle);
虽然泛型示例是最复杂的,但它也是最强大和灵活的。它可以让您轻松地在结构之间共享代码,同时还可以让您拥有特定于一个结构的代码,以便您可以访问其所有字段。