
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
Syllabus
Names and documentation
We will cover Rust's naming conventions as a compression scheme: whatas_,to_, andinto_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# Errorsand# Panicssections and examples thatcargo testkeeps honest.The newtype pattern
You will give distinct things distinct types, so that a call toinsert(bucket, key, value)cannot compile with its arguments in the wrong order. We will cover parsing rather than validating, so that aKeyis evidence the check already ran, and encapsulation, including the doors thatDeref,Default, and a derivedDeserializeleave open.Common traits
A newtype starts with no traits at all, and most of them come back with onederive. We will cover the decisionsderivecannot make for you: whatDebugputs into your logs, the contract betweenHashandEq,CloneagainstCopyas a public commitment, andFromandTryFromas the generic entry points to the constructor you already wrote.Ownership, borrowing, and lifetimes
We will cover what&and&mutactually 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, usingretainand two-pass approaches, and remove the hidden clones from signatures that borrow what they then need to own.RAII and drop guards
You will add transactions tominidb, starting with aTransactionthat borrows the store and cannot outlive it. We will cover drop guards that roll back automatically, drop bombs and why they have to checkthread::panicking, the cases whereDropdoes not run at all, and a closure API that removes the possibility of forgetting to commit.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 noinsertmethod using marker types andPhantomData, then a document writer whose transitions consumeself, which gives you a state machine with no runtime representation.Extension traits
We will cover the orphan rule and why you cannot add a method tostr, 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 asFromStrinstead, and how method resolution decides which implementation a call reaches.Polymorphism
We will cover static and dynamic dispatch,dyncompatibility 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.PhantomData, variance, and brands
We will cover whatPhantomDatadoes 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.
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

