How to avoid code duplication of different structs with semantically equal fields/properties?

This looks like a perfect case for generics.

You can make a single struct like this:

struct ShapeRenderer<T: Shape> {
    canvas: Canvas,
    origin: Point,
    shape: T,
}

Note that I have bounded the generic type T by a trait Shape (that you would have to create). You can put any bounds here you like (or no bounds at all), but you will be restricted to using members of those traits.

Anything that you want to be able to access in your shapes would need to be exposed by Shape. For example, if you need the center and area, then the trait definition might look like this:

trait Shape {
    fn center(&self) -> (f64, f64);
    fn area(&self) -> f64;
}

If that is not enough flexibility, you could also give ShapeRenderer special behavior for only specific shapes. For example:

impl ShapeRenderer<Rectangle> {
    fn n_sides(&self) -> u32 {
        4
    }
}

Note that inside this impl, we have access to all the fields of Rectangle, not just the functions in Shape.


Alternatively, you could create a base struct and then include it as a member of your final structs:

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

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

struct RectangleRenderer {
    renderer: Renderer,
    shape: Rectangle,
}

This is the closest thing in Rust to standard inheritance.


Thirdly, if you only care about code duplication when creating these structs and don’t want them to share anything but fields, you could use a macro:

macro_rules! make_renderer {
    ($name: ty, $shape: ty) => (
        struct $name {
            canvas: Canvas,
            origin: Point,
            shape: $shape,
        }
    );
}

make_renderer!(CircleRenderer, Circle);
make_renderer!(RectangleRenderer, Rectangle);

While the generics example is the most complicated, it is also the most powerful and flexible. It lets you easily have code shared between your structs, while also letting you have code specific to one that gives you access to all its fields.

Leave a Comment