Project 5: Type-State Builder Pattern (Make Invalid States Unrepresentable)
An HTTP request builder where the type system ensures you can’t send a request without setting required fields—illegal states are compile-time errors, not runtime exceptions.
Quick Reference
| Attribute | Value |
|---|---|
| Primary Language | Rust |
| Alternative Languages | Haskell (similar type system power) |
| Difficulty | Level 2: Intermediate |
| Time Estimate | 1 week |
| Knowledge Area | Type System / Generics / API Design |
| Tooling | Rust type system, PhantomData |
| Prerequisites | Project 1 completed, basic generics understanding |
What You Will Build
An HTTP request builder where the type system ensures you can’t send a request without setting required fields—illegal states are compile-time errors, not runtime exceptions.
Why It Matters
This project builds core skills that appear repeatedly in real-world systems and tooling.
Core Challenges
- Encoding states as zero-sized types → maps to PhantomData and marker traits
- Transitioning between states via method chaining → maps to consuming self and returning new types
- Making the API impossible to misuse → maps to compile-time invariant enforcement
- Keeping the API ergonomic despite type complexity → maps to good generic design
Key Concepts
- Type-state pattern: “Rust for Rustaceans” Chapter 3 - Jon Gjengset
- PhantomData: “The Rust Programming Language” Chapter 19 - Steve Klabnik
- Zero-sized types: “Programming Rust, 2nd Edition” Chapter 11 - Jim Blandy
- Builder pattern in Rust: “Rust API Guidelines” - rust-lang.github.io
Real-World Outcome
// This COMPILES ✅
let response = HttpRequest::new()
.method(Method::POST) // State: HasMethod
.url("https://api.example.com") // State: HasUrl
.header("Content-Type", "application/json")
.body(json!({"name": "Alice"})) // State: HasBody
.send() // Only available when all required fields set!
.await?;
// This DOES NOT COMPILE ❌
let response = HttpRequest::new()
.url("https://api.example.com")
.send() // Error: `send` not found—method not set!
.await?;
// Compiler error:
// error[E0599]: no method named `send` found for struct
// `HttpRequest<NoMethod, HasUrl>` in the current scope
// |
// 45 | .send()
// | ^^^^ method not found in `HttpRequest<NoMethod, HasUrl>`
// |
// = note: `send` requires `HttpRequest<HasMethod, HasUrl>`
Implementation Guide
- Reproduce the simplest happy-path scenario.
- Build the smallest working version of the core feature.
- Add input validation and error handling.
- Add instrumentation/logging to confirm behavior.
- Refactor into clean modules with tests.
Milestones
- Milestone 1: Minimal working program that runs end-to-end.
- Milestone 2: Correct outputs for typical inputs.
- Milestone 3: Robust handling of edge cases.
- Milestone 4: Clean structure and documented usage.
Validation Checklist
- Output matches the real-world outcome example
- Handles invalid inputs safely
- Provides clear errors and exit codes
- Repeatable results across runs
References
- Main guide:
LEARN_RUST_DEEP_DIVE.md - “Rust for Rustaceans” by Jon Gjengset