Learn by building. Understand Rust's core types by implementing them yourself,
from simple enums to async runtime internals.
8 Chapters • 191 Tests • 100% Safe APIs
libr0 = lib rust from zer0
We're building Rust's library (lib) from zero, one type at a time.
Learn by building. The best way to understand something is to implement it yourself. This guide takes you through Rust's fundamental types from scratch, starting with simple enums and progressing to async runtime internals. Each chapter builds on the previous one.
-
De-abstract the Abstractions - Rust concepts aren't magic. Box? Just alloc + dealloc in a struct. RefCell? Just a counter and panic! when rules break. We show you exactly how it works.
-
Show the Underlying Reality - What's in memory? Show the layout. What does the CPU do? Explain the instruction or syscall. What does the compiler generate? Show the desugaring.
-
Concrete Over Abstract - When abstractions are hard to grasp, we use good representative examples. Understanding comes from working code, not theory.
-
Address Common Confusions - If people commonly misunderstand something, we call it out explicitly with clear explanations and examples.
Recommended resources:
Since this guide relies heavily on memory layout, this is a great reference for visualizing each type in Rust:
- cheats.rs/#memory-layout - Visual memory layouts for Rust types
For low-level concepts (syscalls, process memory, blocking vs non-blocking operations):
- Rust has no null - The language itself doesn't have null.
Option<T>is just a regular enum we can implement ourselves. - String is just a struct -
String,Vec,Box,Rc- they're all normal structs you can build yourself. Nothing magical. - Rust doesn't know what "threads" or "tasks" are - The language only knows
SendandSynctraits. Thread safety comes from library functions (often calledspawn) requiring these traits in their signatures. Threads and tasks? Just library constructs. - Heap vs Stack - Creating a struct or array? That's on the stack. Unsafe allocator calls (
alloc/dealloc) put things on the heap.Box,Vec, andRcare just safe wrappers. - Cell doesn't allocate -
Cell<T>andRefCell<T>live wherever you put them - stack, heap, or inside other structs. They're just wrappers. - PhantomData isn't scary - Go to definition on stdlib types and you'll see it everywhere. What is it? Just a lifetimes marker. That's it. Nothing magical.
- Slices are special - Unlike everything else here,
&[T]is a language primitive. You can't fully replicate it in user code. - String is literally Vec<u8> - It's just a
Vecwith a UTF-8 guarantee. Same memory layout, sameptr/len/capacity. - async is rewriting your code -
async fndesugars into a state machine. The compiler transforms your code completely. - No async runtime included - Rust only provides the
Futuretrait. Tokio? That's just a library anyone can write.
Read the full book at arinal.github.io/libr0
-
Option - The simplest enum
- Pattern matching basics
Some(T)andNonevariants- Implementing
map,and_then,unwrap_or,filter,as_ref,take
-
Result - Error handling
Ok(T)andErr(E)variants- The
?operator - Converting between
OptionandResult - Error propagation patterns
-
Box - Heap allocation
- Stack vs Heap
- The
DerefandDerefMuttraits Dropfor cleanup- Why recursive types need
Box - Trait objects basics
-
Vec - Growable arrays
- Heap allocation without
Box - Direct use of allocator APIs (alloc/dealloc)
ptr,len, andcapacity- Growing and shrinking
Stringis justVec<u8>- Slices:
&[T]and&str
- Heap allocation without
-
Cell - Copy-based interior mutability
- What is interior mutability?
UnsafeCell- the foundationgetandsetoperations- When to use
Cell
-
RefCell - Runtime borrow checking
- Interior mutability for any type
- Guard types (
RefandRefMut) - Runtime borrow tracking with counters
- RAII and automatic cleanup
try_borrowfor non-panicking access- Common pitfalls and patterns
-
Rc - Reference counting for shared ownership
- Multiple owners of the same data
- Strong and weak references
- Breaking reference cycles with
Weak Clonesemantics for reference counting- When to use
Rcvs borrowing
-
Rc + RefCell - Shared mutable state (single-threaded)
- Combining reference counting with interior mutability
- Building graph structures with cycles
- Tree with parent pointers using
Weak - Observer pattern implementation
- Common patterns and pitfalls
- Memory leak prevention
-
Closures - Function-like types
Fn- immutable captureFnMut- mutable captureFnOnce- consuming capture- Closure capture mechanics
- When to use each trait
-
Memory Layout - Where your data lives
- Process memory layout (Stack, Heap, Static data)
- Function calls and stack frames
- Raw pointers and heap allocation
- Memory layout visualizations
-
Sized - The
Sizedtrait- What is
Sized? - Dynamically sized types
?Sizedbounds
- What is
The following chapters are planned but not yet implemented. See CLAUDE.md for the full roadmap.
- Arc - Atomic reference counting
- Send and Sync - Thread safety markers
- Dynamic Dispatch - Runtime polymorphism
- Mutex - Mutual exclusion
- Channels - Message passing
- Arc + Mutex - Shared mutable state (multi-threaded)
- Async State Machines - How async/await works
- Future Trait - The async foundation
- Waker - Wake-up mechanism
- Mini Executor - Running futures
- Mini Tokio - Async runtime from scratch
- Basic Rust syntax (functions, structs, enums)
- Understanding of ownership and borrowing
- Familiarity with generics and traits
- Rust Language Cheat Sheet - Quick reference for Rust syntax and concepts
- under-the-hood - Low-level concepts: memory layout, syscalls, blocking vs non-blocking
Each chapter contains:
- Concept explanation - Why this type exists
- Step-by-step implementation - Building it from scratch
- Code examples - Practical usage
- Exercises - Reinforce your understanding
// Example: Our own Option type
enum MyOption<T> {
Some(T),
None,
}
impl<T> MyOption<T> {
fn map<U, F: FnOnce(T) -> U>(self, f: F) -> MyOption<U> {
match self {
MyOption::Some(x) => MyOption::Some(f(x)),
MyOption::None => MyOption::None,
}
}
}rustlib/
├── docs/ # Chapter documentation
│ ├── 01-option.md # Chapter 1: Option
│ ├── 02-result.md # Chapter 2: Result
│ ├── 03-box.md # Chapter 3: Box
│ ├── 04-vec.md # Chapter 4: Vec
│ ├── 05-cell.md # Chapter 5: Cell
│ ├── 06-refcell.md # Chapter 6: RefCell
│ ├── 07-rc.md # Chapter 7: Rc
│ ├── 08-rc-refcell.md # Chapter 8: Rc + RefCell
│ └── appendix-closures.md # Appendix: Closures
├── src/ # Library implementations
│ ├── lib.rs # Main library file
│ ├── option.rs # MyOption<T> implementation
│ ├── result.rs # Result0<T, E> implementation
│ ├── box.rs # Box0<T> implementation
│ ├── vec.rs # Vec0<T> implementation
│ ├── cell.rs # Cell0<T> implementation
│ ├── refcell.rs # RefCell0<T> implementation
│ └── rc.rs # Rc0<T> and Weak0<T> implementation
└── README.md # This file
The documentation site is built using mdBook with a custom landing page:
# Build the documentation (use this instead of mdbook build)
./build.sh
# Or build and serve locally
./build.sh serveNote: The build.sh script builds the mdBook and replaces the generated index.html with our custom landing page (from theme/landing.html). When you click "Start Learning", you'll navigate to the actual book content starting with the Introduction.
Each chapter has corresponding example code in the examples/ directory:
# Run individual examples (Chapters 1-8 completed)
cargo run --example 01_option # Chapter 1: Option
cargo run --example 02_result # Chapter 2: Result
cargo run --example 03_box # Chapter 3: Box
cargo run --example 04_vec # Chapter 4: Vec
cargo run --example 05_cell # Chapter 5: Cell
cargo run --example 06_refcell # Chapter 6: RefCell
cargo run --example 07_rc # Chapter 7: Rc
cargo run --example 08_rc_refcell # Chapter 8: Rc + RefCellNote: Example files contain TODO placeholders for you to fill in as exercises. Complete solutions are available on the answers branch:
# Switch to the answers branch to see complete solutions
git checkout answers
# Return to the main branch with exercises
git checkout mainThe project includes comprehensive tests:
# Run all tests
cargo test
# Run only unit tests
cargo test --lib
# Run only documentation tests
cargo test --doc
# Run tests for a specific module
cargo test option
cargo test cellYou can use the completed implementations in your own code:
// Completed types (Chapters 1-8)
use rustlib::option::{Option0, Some, None};
use rustlib::result::{Result0, Ok, Err};
use rustlib::r#box::Box0;
use rustlib::vec::Vec0;
use rustlib::cell::Cell0;
use rustlib::refcell::RefCell0;
use rustlib::rc::{Rc0, Weak0};
// Use the vec0! macro
use rustlib::vec0;
let v = vec0![1, 2, 3];MIT