
M05 Type Driven
- 800 installs
- 1.3k repo stars
- Updated May 24, 2026
- zhanghandong/rust-skills
This is a copy of m05-type-driven by actionbook - installs and ranking accrue to the original listing.
m05-type-driven is a Claude Code skill that teaches type-driven design patterns—PhantomData, newtypes, sealed traits, and type state—so Rust developers encode business rules the compiler enforces before runtime.
About
m05-type-driven is a Rust-focused Claude Code skill from zhanghandong/rust-skills that applies type-driven design to make invalid states unrepresentable at compile time. The skill walks through when to reach for newtypes, marker traits, builder patterns, zero-sized types, and sealed traits instead of runtime checks, with triggers covering type state, compile-time validation, and related Chinese keyword phrases. It sits in Layer 1: Language Mechanics and reframes common errors as design questions about what the type system can prove. Developers reach for m05-type-driven when refactoring primitives into domain types, hardening APIs against misuse, or encoding lifecycle states in Rust structs and traits.
- Teaches type state pattern, PhantomData, newtype, marker traits, and builder pattern
- Transforms primitive obsession and boolean flags into compile-time guarantees
- Shifts validation from runtime checks to construction and state transitions
- Includes 3-layer decision framework: encode constraint, timing of validation, audience of invariant
- Makes invalid states unrepresentable using sealed traits and zero-sized types
M05 Type Driven by the numbers
- 800 all-time installs (skills.sh)
- +6 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/zhanghandong/rust-skills --skill m05-type-drivenAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 800 |
|---|---|
| repo stars | ★ 1.3k |
| Security audit | 3 / 3 scanners passed |
| Last updated | May 24, 2026 |
| Repository | zhanghandong/rust-skills ↗ |
How do you encode invalid states in Rust types?
Leverage Rust's type system to encode business rules so the compiler prevents invalid states before code ever runs.
Who is it for?
Rust developers hardening domain models and APIs who want compiler-checked invariants instead of scattered runtime guards.
Skip if: Teams needing quick scripting prototypes or greenfield apps where runtime validation and speed of iteration outweigh type modeling.
When should I use this skill?
The user mentions type state, PhantomData, newtype patterns, sealed traits, or making invalid states unrepresentable in Rust.
What you get
Type-state structs, newtype wrappers, sealed trait modules, and compile-time invariant patterns in Rust source.
- Type-state Rust modules
- Newtype and sealed-trait API boundaries
By the numbers
- Triggers on 12+ Rust type-system keywords including PhantomData, newtype, sealed trait, and ZST
Files
Type-Driven Design
Layer 1: Language Mechanics
Core Question
How can the type system prevent invalid states?
Before reaching for runtime checks:
- Can the compiler catch this error?
- Can invalid states be unrepresentable?
- Can the type encode the invariant?
---
Error → Design Question
| Pattern | Don't Just Say | Ask Instead |
|---|---|---|
| Primitive obsession | "It's just a string" | What does this value represent? |
| Boolean flags | "Add an is_valid flag" | Can states be types? |
| Optional everywhere | "Check for None" | Is absence really possible? |
| Validation at runtime | "Return Err if invalid" | Can we validate at construction? |
---
Thinking Prompt
Before adding runtime validation:
1. Can the type encode the constraint?
- Numeric range → bounded types or newtypes
- Valid states → type state pattern
- Semantic meaning → newtype
2. When is validation possible?
- At construction → validated newtype
- At state transition → type state
- Only at runtime → Result with clear error
3. Who needs to know the invariant?
- Compiler → type-level encoding
- API users → clear type signatures
- Runtime only → documentation
---
Trace Up ↑
When type design is unclear:
"Need to validate email format"
↑ Ask: Is this a domain value object?
↑ Check: m09-domain (Email as Value Object)
↑ Check: domain-* (validation requirements)| Situation | Trace To | Question |
|---|---|---|
| What types to create | m09-domain | What's the domain model? |
| State machine design | m09-domain | What are valid transitions? |
| Marker trait usage | m04-zero-cost | Static or dynamic dispatch? |
---
Trace Down ↓
From design to implementation:
"Need type-safe wrapper for primitives"
↓ Newtype: struct UserId(u64);
"Need compile-time state validation"
↓ Type State: Connection<Connected>
"Need to track phantom type parameters"
↓ PhantomData: PhantomData<T>
"Need capability markers"
↓ Marker Trait: trait Validated {}
"Need gradual construction"
↓ Builder: Builder::new().field(x).build()---
Quick Reference
| Pattern | Purpose | Example |
|---|---|---|
| Newtype | Type safety | struct UserId(u64); |
| Type State | State machine | Connection<Connected> |
| PhantomData | Variance/lifetime | PhantomData<&'a T> |
| Marker Trait | Capability flag | trait Validated {} |
| Builder | Gradual construction | Builder::new().name("x").build() |
| Sealed Trait | Prevent external impl | mod private { pub trait Sealed {} } |
Pattern Examples
Newtype
struct Email(String); // Not just any string
impl Email {
pub fn new(s: &str) -> Result<Self, ValidationError> {
// Validate once, trust forever
validate_email(s)?;
Ok(Self(s.to_string()))
}
}Type State
struct Connection<State>(TcpStream, PhantomData<State>);
struct Disconnected;
struct Connected;
struct Authenticated;
impl Connection<Disconnected> {
fn connect(self) -> Connection<Connected> { ... }
}
impl Connection<Connected> {
fn authenticate(self) -> Connection<Authenticated> { ... }
}---
Decision Guide
| Need | Pattern |
|---|---|
| Type safety for primitives | Newtype |
| Compile-time state validation | Type State |
| Lifetime/variance markers | PhantomData |
| Capability flags | Marker Trait |
| Gradual construction | Builder |
| Closed set of impls | Sealed Trait |
| Zero-sized type marker | ZST struct |
---
Anti-Patterns
| Anti-Pattern | Why Bad | Better |
|---|---|---|
| Boolean flags for states | Runtime errors | Type state |
| String for semantic types | No type safety | Newtype |
| Option for uninitialized | Unclear invariant | Builder |
| Public fields with invariants | Invariant violation | Private + validated new() |
---
Related Skills
| When | See |
|---|---|
| Domain modeling | m09-domain |
| Trait design | m04-zero-cost |
| Error handling in constructors | m06-error-handling |
| Anti-patterns | m15-anti-pattern |
Related skills
How it compares
Pick m05-type-driven when modeling invariants in Rust types; use general refactoring skills when the goal is syntax cleanup without domain encoding.
FAQ
What is type-driven design in Rust?
m05-type-driven teaches type-driven design in Rust: model domain states and rules in types so the compiler rejects invalid combinations. Patterns include newtypes, type state, PhantomData, marker traits, and sealed traits instead of runtime checks.
When should Rust developers use newtypes over primitives?
m05-type-driven recommends newtypes when primitive obsession hides invariants like IDs, currencies, or lifecycle states. Wrapping primitives in distinct types lets Rust catch mix-ups at compile time without extra runtime validation.
Does m05-type-driven replace all runtime validation?
m05-type-driven prioritizes compile-time invariants for states the type system can represent, but external input still needs parsing and boundary checks. The skill asks whether each error can move from runtime guards into types first.
Is M05 Type Driven safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.