Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust API Design Guidebook

A consumer-first field guide to clear, expressive, and difficult-to-misuse Rust APIs.

Consumer firstReal Rust librariesSource trails

Libraries are a large part of what makes a programming language pleasant—or painful—to use. A library can perform impressive work internally and still offer an awkward interface. Another can make a difficult problem feel natural by choosing the right types, names, defaults, and boundaries.

This guide studies those choices from the consumer’s perspective. It begins with complete programs and their observed behavior. For the strongest examples, it then follows one call into the public types and implementation mechanisms that explain the experience.

What does this API make easy, what does it make impossible, and what does its representation teach the compiler about the problem?

An API is a representation. It maps concepts from a problem domain into constructs in a programming language. A fixed set of choices might become an enum. Ordinary absence might become Option<T>. Explainable failure might become Result<T, E>. A valid sequence of resource operations might become methods that consume one state and return another.

Different representations support different tasks. Suppose an API accepts one of three primary colors. It could accept a string:

#![allow(unused)]
fn main() {
fn to_rgb(color: &str) -> Result<Rgb, UnknownColor>
}

Or it could accept a value whose possibilities correspond exactly to the domain:

#![allow(unused)]
fn main() {
enum PrimaryColor {
    Red,
    Yellow,
    Blue,
}

fn to_rgb(color: PrimaryColor) -> Rgb
}

The string contains far more values than the domain, so every consumer can ask for "mauve-ish". The enum removes that mismatch. Once a caller has a PrimaryColor, conversion cannot fail because of an unknown color.

Good APIs keep related facts consistent. An event name must agree with its payload. A protocol state must agree with the operations currently permitted. A route must agree with the values extracted for its handler. A guard must not outlive the resource access it grants. Weak APIs leave these relationships in comments, strings, or conventions. Stronger APIs choose representations that make inconsistent combinations difficult—or impossible—to construct.

Maximum type-level enforcement is not automatically best. It can create more types, longer compiler errors, slower builds, or an unpleasant common case. Good API design is the search for a representation that supports the consumer’s actual tasks at an acceptable cost.

The book has three working modes:

atlas specimen
    complete consumer program → observed behavior → design questions

library lineage
    related crates → different abstraction boundaries → shared vocabulary

deep dive
    consumer call → public signature → relevant source → tradeoff

Learn how the guide evaluates APIs →

The framing draws especially from Elegant Library APIs in Rust, Pascal Hertleif’s practical criteria for usable libraries, and Type-Driven API Design in Rust, Will Crichton’s treatment of APIs as representations that keep related elements consistent.