Simple Rust Patterns


Simple Patterns

I’ve been using Rust on-again-off-again for several years now. I’m not ashamed to say that a lot of my Rust journey has mirrored my early C++ journey… I just add sigils (or take them away) until the compiler stops complaining. I also read The Rust Book but, again, to be honest, I usually do a very surface reading until I think I’ve figured out how to solve an immediate problem. But I’ve got some time finally and I’m going back through and updating/cleaning my mental model for how things work. Hopefully someone else well find this useful too.

When I’m learning a new language I generally think of two specific patterns I want to know. “How do I make a concrete implementation of an action and then pass that to something else” (Strategy) and “How do I make a concrete implementation of a thing when I don’t know what I need up-front?” (Factory).

Strategy

use std::cell::RefCell;


pub trait Strategy {
    fn do_something(&self);
}

struct TypeA {}
struct TypeB {}
struct TypeC {
    internal: RefCell<i32>
}

impl Strategy for TypeA {
    fn do_something(&self) {
        println!("A doing a thing");
    }
}

impl Strategy for TypeB {
    fn do_something(&self) {
        println!("B doing a thing");
    }
}

impl Strategy for TypeC {
    fn do_something(&self) {
        // "Internal mutability"
        // Allows a change behind a &self reference
        // without needing to change API to &mut self.
        let mut underlying_value = self.internal.borrow_mut();
        println!("C doing a thing {}", *underlying_value);
        (*underlying_value) += 1;
    }
}

// The Generic version uses monomorphism to do everything
// at compile time.
struct Executor<T:Strategy> {
    strategy: T
}

impl <T:Strategy> Executor<T> {
    fn new(input: T) -> Self {
        Executor { strategy: input }
    }

    fn execute(&self) {
        self.strategy.do_something();
    }
}

// The dyn version uses vtables and does everything
// at runtime.
struct ExecutorWithDyn<'a> {
    strategy: &'a dyn Strategy
}

impl <'a> ExecutorWithDyn<'a> {
    fn new(input: &'a impl Strategy) -> Self {
        ExecutorWithDyn { strategy: input }
    }

    fn execute(&self) {
        self.strategy.do_something();
        // This also works
        // let e = &self.strategy;
        // (*e).do_something();
    }
}

fn main() {
    let a = TypeA {};
    let b = TypeB {};
    let c = TypeC {internal: RefCell::new(0)};

    let e = Executor::new(a);
    e.execute();

    let e1 = ExecutorWithDyn::new(&b);
    e1.execute();

    let e2 = ExecutorWithDyn::new(&c);
    e2.execute();
    e2.execute();
    e2.execute();
    e2.execute();
}

Factory

struct Factory {}

pub trait SomeImpl {
    fn do_a_thing(&mut self);
}

struct TypeA {}
impl SomeImpl for TypeA {
    fn do_a_thing(&mut self) {
        println!("Type A did a thing");
    }
}

impl Default for TypeA {
    fn default() -> Self {
        Self {  }
    }
}

struct TypeB {}
impl SomeImpl for TypeB {
    fn do_a_thing(&mut self) {
        println!("Type B did a thing");
    }
}

impl Default for TypeB {
    fn default() -> Self {
        Self {  }
    }
}

impl Factory {
    pub fn build(&self, input: i32) -> Box<dyn SomeImpl> {
        if input % 2 == 0 {
            return Box::new(TypeA{});
        } else {
            return Box::new(TypeB{});
        }
    }

    pub fn build_generics<T:SomeImpl+Default>(&self) -> Box<T> {
        return Box::new(T::default())
    }
}
fn main() {
    println!("Factory");

    let f = Factory{};
    let mut r = f.build(3);
    r.do_a_thing();

    // It's cool this works, but again, you have to
    // know it at compile time.
    let mut r1 = f.build_generics::<TypeA>();
    r1.do_a_thing();
}