Close-up of interlocking gears and chains.

All Rust workshops

Advanced Rust: designing APIs that are hard to misuse

2 days Bookable for teams – on-site or remote

About this workshop

Every rule your domain has is enforced somewhere: in a comment, in a code review, in a runtime check, or in the type system. Rust has an unusually powerful set of tools for pushing those rules into the compiler, and most Rust code uses only a small fraction of them.

This workshop is about designing Rust APIs that other people, including your future self, cannot get wrong. You will build one library over the course of the day: minidb, a small embedded key-value store in the spirit of redb or sled. It starts as the kind of code anyone would write in an afternoon, with HashMaps, &str parameters, and Option everywhere. By the end of the workshop, forgetting to commit a transaction is a compile error, a key from one store cannot be used with another, and the only way to hold a value you should not have is to write unsafe.

Lifetimes are assumed. Elision, non-lexical borrows, and higher-ranked bounds are covered where they come up, because API design keeps running into them.

The workshop is designed for developers who are comfortable writing Rust and want to get better at designing APIs that other people have to live with. If you are new to Rust instead, start with Learn Rust, starting from scratch.

Bookable for teams

2 days

On-site or remote

Book for your team

Syllabus

  1. Names and documentation

    We will cover Rust's naming conventions as a compression scheme: what as_, to_, and into_ promise about cost and ownership, which words to borrow from the standard library, and which prefixes carry no information. You will also write doc comments for a caller who cannot see the body, including # Errors and # Panics sections and examples that cargo test keeps honest.
  2. The newtype pattern

    You will give distinct things distinct types, so that a call to insert(bucket, key, value) cannot compile with its arguments in the wrong order. We will cover parsing rather than validating, so that a Key is evidence the check already ran, and encapsulation, including the doors that Deref, Default, and a derived Deserialize leave open.
  3. Common traits

    A newtype starts with no traits at all, and most of them come back with one derive. We will cover the decisions derive cannot make for you: what Debug puts into your logs, the contract between Hash and Eq, Clone against Copy as a public commitment, and From and TryFrom as the generic entry points to the constructor you already wrote.
  4. Ownership, borrowing, and lifetimes

    We will cover what & and &mut actually promise, lifetime elision and the anonymous lifetime, and the receiver as part of the API. You will work through aliasing against mutation from the inside, using retain and two-pass approaches, and remove the hidden clones from signatures that borrow what they then need to own.
  5. RAII and drop guards

    You will add transactions to minidb, starting with a Transaction that borrows the store and cannot outlive it. We will cover drop guards that roll back automatically, drop bombs and why they have to check thread::panicking, the cases where Drop does not run at all, and a closure API that removes the possibility of forgetting to commit.
  6. Typestate

    We will cover moving the state of a value into its type, so that the operations which are illegal in that state do not exist. You will build a read-only transaction with no insert method using marker types and PhantomData, then a document writer whose transitions consume self, which gives you a state machine with no runtime representation.
  7. Extension traits

    We will cover the orphan rule and why you cannot add a method to str, then the extension trait pattern that works around it, including implementing for the unsized type and a blanket impl over another trait. We will also cover when to reach for a standard trait such as FromStr instead, and how method resolution decides which implementation a call reaches.
  8. Polymorphism

    We will cover static and dynamic dispatch, dyn compatibility and the signatures it rules out, and how to keep a generic function thin so that monomorphisation does not duplicate its body once per type. You will also seal a trait, and we will cover when a closed set is better expressed as an enum.
  9. PhantomData, variance, and brands

    We will cover what PhantomData does to size, auto traits, drop checking, and variance. You will give a handle that owns its data the borrowing behaviour of a reference, then use a branded lifetime and a higher-ranked bound so that keys built against one store cannot be used with another.

Your mentor

Luca Palmieri

Principal Engineering Consultant

Luca Palmieri builds technology products for a living. His current focus is on backend development, software architecture and the Rust programming language. He is the author of "Zero to Production in Rust".

Who we are

Mainmatter has been active in the Rust ecosystem for years, creating the 100 Exercises to Learn Rust course, the C to Rust Migration Book organizing EuroRust, and contributing to open source.

We help teams adopt and build on Rust with confidence via trainings and workshops, hands-on team reinforcement, codebase assessments, and more.

We have trained developers at

Bookable for teams

2 days

On-site or remote

Book for your team

Can't find the right workshops for your team?

We are happy to tailor the curriculum to your needs. We can combine multiple workshops, pick and choose modules, as well as develop new content if desired.
Get in touch

Book this workshop

Our mentors look forward to working with your team and unlocking new capabilities.

Send us a message

Your message is being sent…

Unable to send message.

Please try again later or contact us at [email protected]

Thank you!

We will be in touch soon.

Advanced Rust: designing APIs that are hard to misuse