如何避免具有语义相等的字段/属性的不同结构的代码重复?

xet*_*a11 2 inheritance struct code-duplication rust

鉴于以下两个结构:

pub struct RectangleRenderer {
    canvas: Canvas,
    origin: Point,
    shape: Rectangle,
}

pub struct CircleRenderer {
    canvas: Canvas,
    center: Point,
    shape: Circle,
}
Run Code Online (Sandbox Code Playgroud)

由于我来自爪哇,我会从中提取基类ShapeRenderer淘汰者和应用领域canvas,并origin到,虽然具体类型将继续把他们的领域shape。在这种情况下,Rust的最佳实践是什么,因为特征仅起到类似于界面的作用,因此不允许属性/字段?

pah*_*olg 5

对于仿制药来说,这似乎是一个完美的案例。

您可以像这样制作单个结构:

struct ShapeRenderer<T: Shape> {
    canvas: Canvas,
    origin: Point,
    shape: T,
}
Run Code Online (Sandbox Code Playgroud)

请注意,我已将通用类型限制T为特征Shape(必须创建)。您可以在此处放置任何喜欢的界限(或完全没有界限),但是将只能使用这些特征的成员。

您希望能够访问形状中的任何内容都需要通过公开Shape。例如,如果您需要中心和区域,那么特征定义可能看起来像这样:

trait Shape {
    fn center(&self) -> (f64, f64);
    fn area(&self) -> f64;
}
Run Code Online (Sandbox Code Playgroud)

如果这还不够灵活,您还可以ShapeRenderer仅对特定形状给出特殊的行为。例如:

impl ShapeRenderer<Rectangle> {
    fn n_sides(&self) -> u32 {
        4
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,在其中impl,我们可以访问的所有字段Rectangle,而不仅限于中的功能Shape


另外,您可以创建一个基本结构,然后将其作为最终结构的成员:

struct Renderer {
    canvas: Canvas,
    origin: Point,
}

struct CircleRenderer {
    renderer: Renderer,
    shape: Circle,
}

struct RectangleRenderer {
    renderer: Renderer,
    shape: Rectangle,
}
Run Code Online (Sandbox Code Playgroud)

这是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);
Run Code Online (Sandbox Code Playgroud)

虽然泛型示例最复杂,但是它也最强大和灵活。它使您可以轻松地在结构之间共享代码,同时还使您拥有特定于某个代码的代码,从而可以访问其所有字段。