Type System Fundamentals

Type-Level Design

Type-level design encodes domain rules, state-machine transitions, and capability permissions into compile-time types and traits so that invalid operations fail to compile rather than panicking or returning errors at runtime.

Core Techniques of Type-Level Programming in Rust

Technique Mechanism Runtime Overhead Example
Typestate Pattern Zero-sized state marker types (Locked, Unlocked) on a generic struct Resource<State> 0 bytes Calling .read_data() on Connection<Disconnected> does not exist (E0599)
Sealed Marker Traits Empty traits implemented only for valid type combinations 0 bytes Restricting which state transitions or pin configurations are legal
Capability Tokens (ZSTs) Zero-sized struct with private constructor passed to privileged methods 0 bytes Proving init() or authentication ran before calling execute(token)
Const Generics Matrix<const R: usize, const C: usize> 0 bytes Matrix multiplication mul<const K: usize>(&Matrix<R, C>, &Matrix<C, K>) -> Matrix<R, K> checked at compile time

Typestate Pattern: Compile-Time State Machine Enforcement

By parameterizing a struct HttpRequest<State> over zero-sized state structs (Draft, ReadyToSend) and consuming self by value on each transition (fn finalize(self) -> HttpRequest<ReadyToSend>), the compiler guarantees that:

  1. An unconfigured HttpRequest<Draft> cannot be sent (.send() is only defined on impl HttpRequest<ReadyToSend>).
  2. A finalized request cannot be modified or finalized twice (because finalize(self) moved and consumed the Draft value).
use std::marker::PhantomData;

// Zero-sized state markers (occupy 0 bytes at runtime):
pub struct Draft;
pub struct ReadyToSend;

pub struct Request<State> {
    url: String,
    _state: PhantomData<State>,
}

impl Request<Draft> {
    pub fn new(url: &str) -> Self {
        Self {
            url: url.to_owned(),
            _state: PhantomData,
        }
    }

    // Consumes `Request<Draft>` and returns `Request<ReadyToSend>`:
    pub fn finalize(self) -> Request<ReadyToSend> {
        Request {
            url: self.url,
            _state: PhantomData,
        }
    }
}

impl Request<ReadyToSend> {
    // `.send()` ONLY exists on `Request<ReadyToSend>`!
    pub fn send(self) -> String {
        format!("200 OK from {}", self.url)
    }
}

fn main() {
    let draft = Request::<Draft>::new("https://api.example.com/v1");
    // `draft.send()` would not compile here (`no method named send found for Request<Draft>`)!

    let ready = draft.finalize();
    println!("Response: {}", ready.send());
}
Response: 200 OK from https://api.example.com/v1

Dimension-Checked Matrix Multiplication with Const Generics

#[derive(Debug)]
struct Matrix<const ROWS: usize, const COLS: usize>([[i32; COLS]; ROWS]);

impl<const R: usize, const C: usize> Matrix<R, C> {
    // Inner dimension `C` must match between `Self` (R x C) and `rhs` (C x K):
    fn mul<const K: usize>(&self, rhs: &Matrix<C, K>) -> Matrix<R, K> {
        let mut out = [[0; K]; R];
        for r in 0..R {
            for k in 0..K {
                for c in 0..C {
                    out[r][k] += self.0[r][c] * rhs.0[c][k];
                }
            }
        }
        Matrix(out)
    }
}

fn main() {
    let a = Matrix([[1, 2, 3], [4, 5, 6]]);       // 2 x 3
    let b = Matrix([[1, 0], [0, 1], [1, 1]]);     // 3 x 2

    let product: Matrix<2, 2> = a.mul(&b);        // 2 x 2 (mismatched dimensions fail at compile time!)
    println!("2x2 product: {product:?}");
}
2x2 product: Matrix([[4, 5], [10, 11]])