Introduction

Rust is a powerful and expressive language. However its unique ownership model can be a challenge for experienced or novice programmer alike.

In this book we cover a variety of design patterns that can help solve many problems commonly encountered when writing Rust code.

Status

This book is in early development. As such, the structure and contents are subject to change.

If you spot any errors or omissions, please feel free to suggest an edit.

License

This book is released under the MIT License.

Common Patterns

New

Idea: Use an associated function as a type constructor.


#![allow(unused)]
fn main() {
struct MyType {
    value: Vec<u32>
}

impl MyType {
    /// Create a new [`MyType`].
    fn new() -> Self {
        MyType { value: Vec::new() }
    }
}

// A new instance of `MyType`
let my_class = MyType::new();
}

Newtype

Idea: Wrap an existing type in a struct to form a new type.

This allows you to use Rust's type checker to ensure that the correct type is used.


#![allow(unused)]
fn main() {
struct AccountId(u64);

let account_id = AccountId(0x1234567812345678);
}

Block expressions

Idea: Scope temporary variables in a block expression and return the result


#![allow(unused)]
fn main() {
use std::sync::{Arc, Mutex};
use std::thread;

let counter = Arc::new(Mutex::new(0u32));

let t = {
    // Temporary variables
    let counter = Arc::clone(&counter);

    // Block return value
    thread::spawn(move || {
        *counter.lock().unwrap() += 1;
    })
};

t.join().unwrap();
}

Inner

Idea: Store a type's internal state in a private struct.


#![allow(unused)]
fn main() {
/// Public facing type.
pub struct Public(Inner);

/// Public API.
impl Public {
    pub fn new() -> Self {
        Public(Inner { state: 0 })
    }
}

/// Private inner state.
struct Inner {
    state: u32
}

/// Private API.
impl Inner {
    fn increment(&mut self) {
        self.state += 1;
    }
}
}

Multithreading Patterns

Multithreading in Rust introduces some unique challenges. Unlike many other lanaguages, the compiler is strict on enforcing safe access to shared values.

Move

Idea: Move ownership of a value exclusively to a new thread.


#![allow(unused)]
fn main() {
use std::thread;

let values = vec![1, 2, 3, 4, 5];

let t = thread::spawn(move || {
    // Thread takes exclusive ownership of `values`
    for value in values {
        println!("{}", value);
    }
});

t.join();
}

Threadsafe

Idea: Make a type threadsafe by protecting the internal state with a Mutex.

This type implements Clone, so it can be easily shared between threads.


#![allow(unused)]
fn main() {
use std::sync::{Arc, Mutex};

#[derive(Clone)]
struct Counter(Arc<Mutex<u64>>);

impl Counter {
    fn new() -> Self {
        // Internal state is protected by a Mutex
        Counter(Arc::new(Mutex::new(0)))
    }

    fn value(&self) -> u64 {
        *self.0.lock().unwrap()
    }

    fn increment(&self) {
        *self.0.lock().unwrap() += 1;
    }
}
}

Handle

Idea: Split a class into an exclusive owned type and a thread-safe handle.


#![allow(unused)]
fn main() {
use std::io;
use std::sync::mpsc;

/// Worker with exclusive access to a resource.
struct Worker {
    channel: mpsc::Receiver<Message>
}

impl Worker {
    /// Returns ([`Worker`], [`Handle`]) pair.
    fn new() -> (Self, Handle) {
        // Channel for sending messages to the worker.
        let (tx, rx) = mpsc::channel();

        (Worker { channel: rx }, Handle { channel: tx })
    }

    /// Function which takes exclusive access of [`Worker`].
    fn run(&mut self) {
        loop {
            while let Ok(message) = self.channel.recv() {
                match message {
                    Message::Foo(value) => println!("Foo: {}", value),
                }
            }
        }
    }
}

/// Handle for interfacing with the worker.
#[derive(Clone)]
struct Handle {
    channel: mpsc::Sender<Message>
}

impl Handle {
    fn foo(&self, value: i32) {
        self.channel.send(Message::Foo(value)).unwrap()
    }
}

/// Messages that can be sent to [`Worker`].
enum Message {
    Foo(i32)
}
}