Rust API Design Guidebook
A consumer-first field guide to clear, expressive, and difficult-to-misuse Rust APIs.
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.