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:
- An unconfigured
HttpRequest<Draft>cannot be sent (.send()is only defined onimpl HttpRequest<ReadyToSend>). - A finalized request cannot be modified or finalized twice (because
finalize(self)moved and consumed theDraftvalue).
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]])