https://github.com/utkarsh5026/rust-scratch
π¦π A learn-by-doing Rust workspace β pick a concept, get a ladder of 7-9 problems (easy β mastery), and implement each one yourself with staged hints, never handed the solution. Powered by a custom rust-practice coaching skill. π
https://github.com/utkarsh5026/rust-scratch
async-rust claude-code education exercises learn-by-doing learning lifetimes ownership practice rust rust-lang systems-programming
Last synced: 6 days ago
JSON representation
π¦π A learn-by-doing Rust workspace β pick a concept, get a ladder of 7-9 problems (easy β mastery), and implement each one yourself with staged hints, never handed the solution. Powered by a custom rust-practice coaching skill. π
- Host: GitHub
- URL: https://github.com/utkarsh5026/rust-scratch
- Owner: utkarsh5026
- Created: 2026-06-17T12:51:00.000Z (25 days ago)
- Default Branch: master
- Last Pushed: 2026-06-26T03:57:44.000Z (16 days ago)
- Last Synced: 2026-06-26T05:23:15.554Z (16 days ago)
- Topics: async-rust, claude-code, education, exercises, learn-by-doing, learning, lifetimes, ownership, practice, rust, rust-lang, systems-programming
- Language: Rust
- Homepage: https://utkarsh5026.github.io/rust-scratch/
- Size: 660 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Roadmap: ROADMAP.md
Awesome Lists containing this project
README
# rust-scratch
My personal **learn-by-doing** workspace for mastering Rust. Not a product β a
practice gym. I name a concept, get a ladder of problems, and implement each one
myself with coaching (never handed the answer), until I actually understand it.
The whole thing is built around the **`rust-practice` skill** + a structured
mastery curriculum in [`ROADMAP.md`](./ROADMAP.md).
## Where I'm at
## The core loop
1. Name a concept β `practice ` (or `/rust-practice `)
2. A ladder of **7-9 problems** (easy β mastery) is scaffolded into `src/bin/.rs`
3. Implement one rung at a time; run `cargo run --bin ` to check
4. Get **staged hints** if stuck β the solution is revealed only if you ask
5. Each ladder ends in a **build-it-from-scratch capstone**
Every solved rung earns **XP** and feeds a gamified dashboard (`cargo run --bin
stats` or `/stats`): ranks π₯βπ¦ββ¦βπ, achievements (Hint-Free, One-Shot,
Miri-Clean, Capstone, Phase Clear), and a daily **streak**.
```
practice Rc and RefCell # start a new ladder
cargo run --bin rc_refcell # run it β passes the solved rungs, stops at your next todo!
```
## Quick start
```bash
# run the scratch pad (quick throwaway code)
cargo run
# run a specific concept ladder
cargo run --bin cow
# start a fresh concept (in Claude Code)
# "practice lifetimes" or /rust-practice lifetimes
```
## How a concept file works
One file per concept. Each problem is a function you implement + a `check_N()`
that asserts it. `main` runs them in order, so it replays everything you've
solved and stops at the first unfinished rung (its `todo!` panics).
```rust
fn solve_it(...) -> ... {
todo!("your turn") // <- you fill this in
}
fn check_3() { /* asserts solve_it works */ }
fn main() {
check_1();
check_2();
check_3(); // <- run stops here until you implement rung 3
// check_4();
}
```
## The curriculum
[`ROADMAP.md`](./ROADMAP.md) is a 9-phase path from comfortable-with-basics to
advanced Rust engineer. Each item is a ladder to run:
| Phase | Theme |
|-------|-------|
| 0 | Tooling, project structure & testing (features, property tests, fuzzing) |
| 1 | Ownership, conversions & the type system (smart pointers, lifetimes, variance, HRTB) |
| 2 | Traits & generics like a library author (object safety, GATs, const generics) |
| 3 | API & error design (typestate, semver, strings, collections) |
| 4 | Concurrency (atomics, memory ordering, lock-free, rayon) |
| 5 | Async internals (`Future`, `Pin`, build an executor, tower) |
| 6 | Unsafe & the machine (raw pointers, layout, Stacked Borrows, implement `Vec`) |
| 7 | Performance & low-level craft |
| 8 | Metaprogramming (declarative & proc macros) |
| 9 | Specialization tracks (no_std/embedded Β· web Β· WASM Β· CLI) |
| 10 | Capstones |
~90 ladders Β· ~700 hands-on problems.
Say **"what's next"** to pull the next unchecked item and start its ladder.
## Completed concepts
**Phase 0 β Tooling, project structure & testing**
| Concept | File | Rungs |
|---------|------|-------|
| Modules & visibility | [`src/bin/modules.rs`](./src/bin/modules.rs) | 9 β module tree & paths β `pub` opens a door β field privacy + smart constructor β `use` vs `pub use` re-export β leaking a private type (E0603/E0616) β `pub(crate)`/`pub(super)`/`pub(in path)` β facade pattern β sealed trait via private module β `inventory` mini-library capstone |
| Cargo features & `cfg` | [`src/bin/features_cfg.rs`](./src/bin/features_cfg.rs) | 9 β `cfg!()` macro β `#[cfg]` attribute (twin defs) β first feature flag β `cfg_attr` (serde idiom) β **additivity law** (collide vs accumulate) β missing-symbol trap (gate the call site too) β optional dep `dep:rand` β feature graphs (`default`/`full`/`--no-default-features`) β mini config/output module capstone |
| π₯ Testing | [`src/bin/testing.rs`](./src/bin/testing.rs) + [`practice/testing_lab/`](./practice/testing_lab/) | 9 β `#[cfg(test)] mod tests`/`assert_eq!` β assertion toolbox + custom messages β `Result`-returning tests & `?` β `#[should_panic(expected=β¦)]` β execution control (`#[ignore]`/filter/`--nocapture`/parallelism) β assertion footguns (loose should_panic, float eq, private reach) β integration tests (`tests/`, `common/mod.rs`, E0603 boundary) β doctests (`?`/hidden `#`/`should_panic`/`compile_fail`) β capstone `Ledger` tested every way |
**Phase 1 β Ownership, conversions & the type system**
| Concept | File | Rungs |
|---------|------|-------|
| `Cow` (Clone-on-Write) | [`src/bin/cow.rs`](./src/bin/cow.rs) | 9 β basics β serde zero-copy β reimplement from scratch |
| `Box` & the heap | [`src/bin/box_heap.rs`](./src/bin/box_heap.rs) | 9 β recursive types β `dyn Trait`/`Box::leak` β hand-rolled linked list |
| `Rc` / `Arc` | [`src/bin/rc_arc.rs`](./src/bin/rc_arc.rs) | 9 β shared ownership β cycles & `Weak` β `Arc` β hand-rolled `MyRc` |
| `Cell` / `RefCell` | [`src/bin/cell_refcell.rs`](./src/bin/cell_refcell.rs) | 9 β interior mutability β borrow panics β hand-rolled `MyRefCell` |
| Conversion traits | [`src/bin/conversions.rs`](./src/bin/conversions.rs) | 9 β `From`/`Into` β `TryFrom` β `AsRef` β mini `serde_json::Value` |
| Lifetimes in depth | [`src/bin/lifetimes_depth.rs`](./src/bin/lifetimes_depth.rs) | 9 β elision β structs β outlives bounds β hand-rolled `StrSplit` |
| `Borrow` / `ToOwned` | [`src/bin/borrow_toowned.rs`](./src/bin/borrow_toowned.rs) | 9 β `HashMap::get(&str)` β contracts β hand-rolled `MyCow` |
| `Drop` & ordering | [`src/bin/drop_ordering.rs`](./src/bin/drop_ordering.rs) | 9 β LIFO/field order β drop flags β rollback-on-drop `Transaction` |
| `Rc>` patterns | [`src/bin/rc_refcell.rs`](./src/bin/rc_refcell.rs) | 10 β shared cell β cycle leak + `Weak` β doubly-linked list w/ iterative Drop |
| HRTB β `for<'a>` | [`src/bin/hrtb.rs`](./src/bin/hrtb.rs) | 9 β implicit `for<'a>` β `DecodeOwned` β parser-combinator capstone |
**Phase 2 β Traits & generics like a library author**
| Concept | File | Rungs |
|---------|------|-------|
| Generic bounds & `where` clauses | [`src/bin/generic_bounds.rs`](./src/bin/generic_bounds.rs) | 9 β bounds β `?Sized` β blanket impls β `IterExt` extension trait |
| Associated types vs generic params | [`src/bin/assoc_vs_generic.rs`](./src/bin/assoc_vs_generic.rs) | 9 β one-impl-per-type vs many β `dyn` assoc pinning β `MyIterator` + `Map` |
| Blanket impls & coherence | [`src/bin/blanket_coherence.rs`](./src/bin/blanket_coherence.rs) | 9 β `From`β`Into` β orphan rule β sealed extension trait |
| Static vs dynamic dispatch | [`src/bin/dispatch.rs`](./src/bin/dispatch.rs) | 9 β monomorphization vs vtable β object safety β static/dynamic/enum pipeline |
| Closures & `Fn`/`FnMut`/`FnOnce` | [`src/bin/closures.rs`](./src/bin/closures.rs) | 9 β capture modes β `Fn β FnMut β FnOnce` β desugar by hand β `impl Fn` vs `Box` β fn-pointer coercion β `Box` event dispatcher |
| `impl Trait` & RPIT | [`src/bin/impl_trait.rs`](./src/bin/impl_trait.rs) | 9 β APIT (caller picks) vs RPIT (callee picks one hidden type) β turbofish footgun β return closures/chains β one-type rule (Box/Vec/`Either`) β 2024 lifetime auto-capture + `use<>` β `async fn` β‘ `-> impl Future` β RPITIT & async-fn-in-trait (not dyn-safe) β combinator toolkit |
| Marker & auto traits | [`src/bin/marker_auto_traits.rs`](./src/bin/marker_auto_traits.rs) | 9 β marker permissions β `Copy`/`Sized`/`?Sized` β structural `Send`/`Sync` β opt-out/opt-in with `PhantomData` and `unsafe impl` β sealed typestate capstone |
**Phase 3 β API & error design**
| Concept | File | Rungs |
|---------|------|-------|
| Error handling architecture | [`src/bin/error_arch.rs`](./src/bin/error_arch.rs) | 9 β `Box` β `thiserror`/`anyhow` β mini-anyhow |
| Custom error types | [`src/bin/custom_errors.rs`](./src/bin/custom_errors.rs) | 9 β Display+Error by hand β source chains β `Report` reporter |
| Newtype & zero-cost wrappers | [`src/bin/newtype.rs`](./src/bin/newtype.rs) | 9 β distinct identity β `repr(transparent)` β phantom-typed `Id` |
| Builder pattern | [`src/bin/builder.rs`](./src/bin/builder.rs) | 8 β consuming/`&mut` builders β typestate builder β `ServerConfig` |
| The typestate pattern | [`src/bin/typestate.rs`](./src/bin/typestate.rs) | 9 β ZST markers β sealed states β TCP-like protocol |
| API evolution & semver | [`src/bin/semver.rs`](./src/bin/semver.rs) | 9 β what breaks β `#[non_exhaustive]` β sealed traits β `ApiChangeβBump` engine |
| Collections deep-dive | [`src/bin/collections.rs`](./src/bin/collections.rs) | 9 β `Entry`/`Borrow` lookup β custom `Hash`/`Eq` β open-addressing `MyHashMap` |
| Strings & text | [`src/bin/strings_text.rs`](./src/bin/strings_text.rs) | 9 β `str`/`String` & UTF-8 invariant β char-boundary slicing β `OsStr`/`Path`/`CStr` β `from_utf8` validation β hand-rolled UTF-8 decoder |
**Phase 4 β Concurrency**
| Concept | File | Rungs |
|---------|------|-------|
| Threads & scoped threads | [`src/bin/threads.rs`](./src/bin/threads.rs) | 9 β `spawn`/`join` β `thread::scope` β `parallel_map` (rayon-lite) |
| `Send` & `Sync` deeply | [`src/bin/send_sync.rs`](./src/bin/send_sync.rs) | 9 β auto-derivation β the four quadrants β hand-rolled `SpinLock` |
| `Mutex` / `RwLock` | [`src/bin/mutex_rwlock.rs`](./src/bin/mutex_rwlock.rs) | 9 β guard RAII β `Arc` counter β `RwLock` readers-xor-writer β poisoning & recovery β non-reentrancy β ABBA deadlock + lock ordering β `Condvar` queue β concurrent `Bank` |
| Channels | [`src/bin/channels.rs`](./src/bin/channels.rs) | 9 β `mpsc` send/recv β multi-producer `tx.clone()` β receiver-as-iterator (drop the sender) β `sync_channel` bounded/rendezvous backpressure β the hang + `RecvError`/`SendError` β `try_recv` Empty vs Disconnected β worker pool (`Arc>`) β crossbeam mpmc + `select!` β hand-rolled `Channel` (`Mutex`+`Condvar`) |
| Data parallelism with `rayon` | [`src/bin/rayon_parallel.rs`](./src/bin/rayon_parallel.rs) | 9 β `par_iter().sum()` β `map`/`filter`/`collect` (order preserved) β `reduce`/`fold` (identity closure, fold-then-reduce) β `rayon::join` fork-join primitive β break-even: when parallel LOSES vs WINS β non-associative reduce = silent non-determinism β shared-state wall (`for_each` push won't compile; `collect` vs `Mutex`) β `par_sort` + `par_bridge` β capstone: hand-rolled `parallel_map` + parallel quicksort (`split_at_mut` + `join`) |
| Shared state vs message passing | [`src/bin/concurrency_models.rs`](./src/bin/concurrency_models.rs) | 9 β same counter via `Arc` and mpsc aggregator β ownership transfer = no lock (Mutex guards the QUEUE, not the DATA) β pipeline of stages (EOF cascades as senders drop) β fan-out/fan-in: shared `VecDeque` vs mpsc queue (channel gives blocking + EOF free) β lock held too long (87ms vs 3ms; fat critical section serializes N threads to one) β message-passing footguns (stray sender hang, `RecvError`, `sync_channel` full, the `Arc`-through-channel aliasing trap) β the actor (one owner of a HashMap, command + one-shot reply, no lock) β hybrid (writes via actor, lock-light reads from a published `Arc>>` snapshot) β capstone: one `KvStore: Send+Sync` trait, two impls (`Arc>` vs actor), same 8-thread storm passes both β actor serializes at the queue, lock at the critical section |
_(early standalone demos, not ladders: `src/bin/lifetimes.rs`, `src/bin/traits.rs`)_
## Layout
```
rust-scratch/
βββ README.md β you are here
βββ ROADMAP.md β the mastery curriculum
βββ CLAUDE.md β how Claude Code should drive this project
βββ Cargo.toml
βββ .claude/
β βββ skills/rust-practice/ β the practice-ladder coach
β βββ commands/ β /experiment, /run, /explain, /list
βββ src/
βββ main.rs β scratch pad (cargo run)
βββ bin/ β one file per concept ladder
```
## Slash commands
| Command | Does |
|---------|------|
| `/rust-practice ` | start a coached practice ladder |
| `/stats` | show the gamified dashboard β rank, XP, phases, badges, streak |
| `/list` | show ladders & per-concept progress |
| `/run ` | run an experiment and explain its output |
| `/explain ` | tutor-mode walkthrough of an existing file |
| `/experiment ` | scaffold a one-off demo (not a ladder) |
## Preinstalled dependencies
So ladders never need setup: `tokio`, `futures`, `async-trait`, `anyhow`,
`thiserror`, `serde` + `serde_json`, `reqwest`, `rand`, `tracing`. Add more in
`Cargo.toml` as a concept needs them.