-3

I want to study design pattern use rust language. From this code it works properly but it uses dynamic dispatch. How can I change the code to use static dispatch? Thanks!

trait Shape {
    fn draw(&self);
}

enum ShapeType {
    Rectangle,
    Circle,
}

struct Rectangle {}

impl Shape for Rectangle {
    fn draw(&self) {
        println!("draw a rectangle!");
    }
}

struct Circle {}

impl Shape for Circle {
    fn draw(&self) {
        println!("draw a circle!");
    }
}

struct ShapeFactory;
impl ShapeFactory {
    fn new_shape(s: &ShapeType) -> Box<dyn Shape> {
        match s {
            ShapeType::Circle => Box::new(Circle {}),
            ShapeType::Rectangle => Box::new(Rectangle {}),
        }
    }
}

fn main() {
    let shape = ShapeFactory::new_shape(&ShapeType::Circle);
    shape.draw(); // output: draw a circle!

    let shape = ShapeFactory::new_shape(&ShapeType::Rectangle);
    shape.draw(); // output: draw a rectangle!
}
  • Please improve your question by **showing** what type of code you'd like to write – Shepmaster Feb 21 '22 at 14:52
  • Note that questions should be *self-contained*; please [edit] your question so that it does not rely on external resources. See the [ask] and [mre] pages for guidance. – MisterMiyagi Feb 21 '22 at 15:14

2 Answers2

2

You could do something like this:

trait ShapeFactory {
    type Product: Shape;
    fn new_shape() -> Self::Product;
}

struct CircleFactory;
impl ShapeFactory for CircleFactory {
    type Product = Circle;
    fn new_shape() -> Circle {
        Circle {}
    }
}

// separate method, to demonstrate the factory part a bit better
fn draw_new_shape<F: ShapeFactory>() {
    let shape = F::new_shape();
    shape.draw();
}

fn main() {
    draw_new_shape::<CircleFactory>();
}

(Unlike the enum-approach, this doesn't pre-decide which things could have factory shapes.)

Should you do something like this? Most likely no. I highly doubt the validity of factories as a "design pattern" in Rust.

Caesar
  • 6,733
  • 4
  • 38
  • 44
  • Where is Rectangle? If I need to create it based on Circle? It make me confuse! – minikiller Feb 21 '22 at 15:27
  • Thank for your reply firstly. I am a newbie for rust, so after I read your code I know it is a associated types from rust. Now my question is if I can delete draw_new_shape function. I want to say if I can run its logic in main() function. Thanks. ``` – minikiller Feb 21 '22 at 21:41
  • I left out the Rectangle part for brevity. You can get it by copying the `CircleFactory` and string-replacing `Circle` by `Rectangle`. – Caesar Feb 22 '22 at 10:50
  • @minikiller You can use the factory directly in `main` with `CircleFactory::new_shape` (or `::new_shape` if you want to be explicit). You can, but there's really no point over just using `Circle {}` (In a more complete setup, you'd define and use a `Circle::new`). – Caesar Feb 22 '22 at 10:51
1

In the provided example, I would add

enum StaticShape {
    Rectangle(Rectangle),
    Circle(Circle),
}

impl Shape for StaticShape {
    fn draw(&self) {
        match self {
            StaticShape::Rectangle(sh) => sh.draw(),
            StaticShape::Circle(sh) => sh.draw(),
        }
    }
}

struct StaticShapeFactory;
impl StaticShapeFactory {
    fn new_shape(s: &ShapeType) -> StaticShape {
        match s {
            ShapeType::Rectangle => StaticShape::Rectangle(Rectangle {}),
            ShapeType::Circle => StaticShape::Circle(Circle {}),
        }
    }
}

and in the main() function

    let shape = StaticShapeFactory::new_shape(&ShapeType::Circle);
    shape.draw(); // output: draw a circle!

    let shape = StaticShapeFactory::new_shape(&ShapeType::Rectangle);
    shape.draw(); // output: draw a rectangle!

The StaticShape enum enumerates every possible specific shape that can be handled. In order to behave as a shape, it implements the Shape trait by simply forwarding the call to the specific shape types. The corresponding factory is very similar to the dynamic one; it just builds the specific variant of the returned enum instead of a box.

prog-fh
  • 13,492
  • 1
  • 15
  • 30