Rust fundamentals: a precise 6-week plan for systems-minded data engineers now
Rust rewards precision. This plan gets you productive fast without hand‑wavy theory. It’s optimized for an experienced engineer coming from JVM or Scala, who cares about performance, correctness, and clean mental models.
Ground rules
- Keep it hands on. Every day ships a small artifact.
- Read just enough, code immediately, then measure.
- Prefer standard library and simple crates first. Add frameworks only when you feel the pain.
- Pin to stable Rust. Let cargo choose the current edition when you create a crate; set
rust-versioninCargo.toml. Use nightly only for learning tools likecargo-expand.
Day 0 - your toolchain, verified
Goal: clean install, fast feedback, and a project workspace you enjoy using.
- Install Xcode Command Line Tools on macOS:
| |
- Install Rust via rustup and set stable as default:
| |
- Add the standard components:
| |
- Editor setup:
- IntelliJ IDEA: install the Rust plugin (RustRover tech) and enable rust‑analyzer.
- Turn on format on save and Clippy on check.
- Quality and velocity tools you’ll actually use:
| |
Create a Justfile to make common tasks one‑liners:
| |
The 6‑week ladder
Sprint timeline (journal view)
Use the journal tasks below to track weekly progress directly inside this post. Update statuses in the front matter, and the embedded view will reflect your current state.
No structured tasks found. Add them under journal.tasks in front matter.
Each week is 5 focused days (~90 minutes/day) + one deeper weekend session. By the end you’ll have three production‑grade artifacts. Targets per week are explicit so you know you’re done.
Week 1 - ownership, borrowing, result, modules
You’ll learn: ownership moves, borrowing, lifetimes by feel, pattern matching, modules, Result and error context.
Build: CLI csv-cut that selects columns from a CSV and writes CSV to stdout (keep I/O simple this week).
- Start a workspace
data-toolswith memberscsv-cutandlibcore. - Add crates incrementally:
csv,serde,serde_json,clap. - Practice: accept
&strnotString; returnResult<T, E>; bubble errors with?; attach context usinganyhowor define a small domain error withthiserror. - Stretch: add
--output parquetflag but wire it next week.
Targets:
- Handles headers and empty fields; exits non‑zero on bad indices;
--helpprints usage. - Processes a 1GB CSV without panicking on your machine.
Checkpoint: explain why the borrow checker rejected a mutable and immutable borrow in the same scope, then fix it using narrower scopes or by re‑structuring data.
Week 2 - enums, traits, generics, iterators
You’ll learn: enums as algebraic data types, impl blocks, trait bounds, iterators and adapters, zero‑cost abstractions.
Build: csv-scan that streams CSV to minimal Arrow arrays (no DataFusion yet).
- Library crate
libarrow: write small helpers that convert rows to Arrow arrays/builders. - Add property tests with
proptestand one snapshot test withinstafor a tricky format edge case. - Micro‑bench with
criterionon one hot loop (row parsing → array build).
Targets:
- Arrow crate compiles with a minimal feature set; Criterion report shows a measurable delta after an iterator refactor.
Checkpoint: replace a for loop with iterator combinators and show the perf delta in a Criterion report.
Week 3 - concurrency and messaging
You’ll learn: threads, channels, Send and Sync, data parallelism, cancellation.
Build: parallel CSV→Parquet converter.
- Use
crossbeam-channelto decouple read/transform/write with bounded channels for backpressure. - Use
rayononly for CPU-bound transforms; keep I/O on dedicated threads. - Add
tracingfor structured logs and spans.
Targets:
- Converts 1GB CSV to Parquet within an acceptable wall‑time on your machine; graceful cancellation on drop of sender.
Checkpoint: run cargo flamegraph on a large file and paste the top 3 hot symbols into your notes with what you’ll try next. On macOS, cargo flamegraph may require sudo (dtrace). If blocked, use Instruments or pprof-rs.
Week 4 - async I/O and a tiny service
You’ll learn: Future, executors, async I/O, backpressure, timeouts.
Build: an axum HTTP service with two endpoints.
/healthzreturns 200./sqlstarts as a stub (fixed JSON) to wire timeouts, errors, and graceful shutdown; then integrate DataFusion over local Parquet as a stretch.- Use Tokio runtime, graceful shutdown, request timeouts, and structured errors.
Targets:
- Under a simple load test, p95 stays within your target at N RPS for the stub; DataFusion integration keeps p95 within a relaxed target.
Checkpoint: add a load test and confirm p95 stays within your target under parallel queries.
Week 5 - FFI and Python bridge
You’ll learn: writing a thin Rust core and exposing it to Python.
Build: a Python package fastcsv that wraps your Rust libcore using PyO3 and maturin.
- First:
maturin developfor local import; wheels later. - Ship two functions:
infer_schema(path) -> dict,select_cols(path, cols) -> output_path. - Keep the Python API tiny and well‑typed.
Targets:
- Local import works in a clean venv; function timings beat a pure‑Python baseline on a sample file.
Checkpoint: from a Python REPL, import fastcsv, run it on a sample file, compare timings vs pure Python.
Week 6 - profiling, coverage, supply‑chain hygiene, release
You’ll learn: coverage with LLVM, auditing, dependency hygiene, minimal releases.
Build: a clean release of csv-cut.
- Add coverage gates with
cargo llvm-cov(needsllvm-tools-preview). - Run
cargo audit,cargo outdated,cargo udepsand fix issues. - Produce a macOS arm64 binary locally; for Linux/x86_64 use CI runners. If cross‑compiling is required, try
cross(Docker/Podman needed) orcargo-zigbuildas Plan B. - Tag a release and attach artifacts.
Targets:
- Reproducible build script;
--versionflag; binary ≤ your size budget; audit clean.
Checkpoint: a short CHANGELOG entry that states perf, memory, and behavior changes with numbers.
Capstones you can actually reuse
- DataFusion micro‑warehouse - run SQL on local Parquet for quick analysis and tests.
- High‑throughput CSV→Parquet pipeline - rayon + Arrow + Parquet writer.
- Python bridge - a wheel that exposes your hot path to data scientists.
The mental model that makes Rust click
- Ownership: one owner at a time. Moves transfer ownership. Clones are explicit.
- Borrowing: immutable borrows can alias, mutable borrow is exclusive.
- Lifetimes: mostly inferred. Name them only when the compiler can’t.
- Error handling: make invalid states unrepresentable with types. Use
Resultgenerously, add context, avoidunwrapin library code.
Minimal crate starter
Use this skeleton to avoid yak‑shaving (create it OUTSIDE this website repo, e.g., ~/dev/rust/data-tools):
| |
main.rs:
| |
Hands‑on project setup (outside this repo)
- Create your project folder:
mkdir -p ~/dev/rust && cd ~/dev/rust - Create the crate:
cargo new csv-cut --bin && cd csv-cut - Add deps:
cargo add anyhow clap@4 csv serde serde_json thiserror - Add
rust-toolchain.tomlwithchannel = "stable"and componentsclippy,rustfmt,llvm-tools-preview(optional but recommended). - First run:
cargo run -- --help - Format/lint:
cargo fmt && cargo clippy -D warnings
Sample data.csv
Small sample (for correctness):
| |
Generate a larger CSV locally (approx 5M rows ~ couple hundred MB; adjust N for size):
| |
Tiny tests for --cols and headers
Add dev-dependencies to Cargo.toml:
| |
Create tests/csv_cut_smoke.rs:
| |
Run: cargo test
1–2 GB exercise (Week 1 target)
Create a bigger file (~1–2 GB) by raising N in the Python generator above (e.g., N=40_000_000 may approach ~1.5–2 GB depending on row width) and run:
| |
Note wall-time and CPU usage. If it panics or is too slow, reduce N, confirm correctness first, then profile later in Week 3.
What to read and when
- Start here: The Rust Programming Language, Ownership, Borrowing, Traits, Error Handling.
- Exercises: Rustlings. Pair it with Rust by Example for quick lookups.
- Async: Asynchronous Programming in Rust and Tokio guides.
- Web: axum examples and docs.
- Data: Apache Arrow (minimal features) and Parquet crates, Polars docs, DataFusion user docs and
datafusion-exprcrate (integrate after Week 3). - Unsafe: The Rustonomicon. Read for literacy, not to write unsafe code.
- Testing & Perf: proptest, insta, criterion. Coverage with cargo‑llvm‑cov. Profiling with cargo‑flamegraph.
- Tooling: rustfmt and Clippy. Dependency checks with cargo‑audit, cargo‑outdated, cargo‑udeps. Release with cross and just enough packaging.
Common borrow checker errors and quick fixes
- “cannot borrow as mutable because it is also borrowed as immutable”: shorten the immutable borrow’s scope, or split the data structure so borrows don’t overlap.
- “value moved here”: pass a reference if you only need to read, or
cloneif cheap and correctness matters more than allocations. Better: refactor to return the value to the caller. - “returns a reference to data owned by the function”: return an owned value instead, or thread lifetimes through types explicitly.
Your success checklist
- Code daily. Small loops of build, run, test.
- Write down one thing you learned per day.
- Every Friday:
cargo audit,outdated,udeps, and one micro‑bench. - Every Sunday: refactor one function for clarity. No new features.
Appendix - commands you’ll reuse
| |
Document and commit (this repo only holds documentation)
- Update your weekly notes in this post or add a
content/posts/YYYY/MM/progress-<week>.mdentry. - Commit and push the docs here (not your Rust code):
| |
Sprint board overview
The board below pulls directly from this post’s sprintboard front matter. Update story statuses, task checkboxes, or comments and the JIRA-style view refreshes on each Hugo build.
Shape Arrow builders, iterators, property tests, benchmark-driven ergonomics.
- Implement Arrow RecordBatch helpers
- Add proptest + insta snapshot
- Replace loops with iterators & benchmark
Expose libcore to Python via PyO3/maturin; benchmark against pure Python.
- Expose infer_schema/select_cols via PyO3
- Build wheel with maturin develop
- Benchmark vs pure Python baseline
Coverage gates, supply-chain checks, artifact release, and final metrics.
- Enforce coverage & audit gates
- Produce macOS/Linux binaries + changelog
- Document perf & learning wrap-up
Install stable toolchain, linting, velocity tools, and verify IDE feedback loop.
- Install rustup, set default stable, add clippy/rustfmt
- Configure IDE with rust-analyzer, format on save
- Install cargo-nextest, llvm-cov, audit/outdated/udeps
CLI practice: borrow checker fluency, Result ergonomics, streaming IO.
- Scaffold csv-cut CLI writing to stdout
- Parse args with clap, emit structured errors
- Prove 1–2 GB CSV throughput
Expose DataFusion-backed queries with timeouts, tracing, and load-test targets.
- Ship stub /sql + timeouts
- Integrate DataFusion query executor
- Run load test & capture p95
Channel-based CSV → Parquet pipeline with cancellation & profiling insights.
- Implement bounded channel stages
- Parallel CPU transforms with rayon
- Profile with flamegraph / Instruments
Usage tips
statuscontrols the column (planned,in-progress,review,blocked,done).tasks[].donetoggles the progress bar for each story.commentsacceptauthor,when,bodyfor quick retros.- Customize columns or palette via
sprintboard.columns.
Notes for readers: This plan sticks to stable Rust and conservative crates so you learn fundamentals first. Add frameworks only after Week 3. Keep the repo small, with one Justfile and CI that runs clippy, tests, coverage, and audit. On macOS, cargo flamegraph may require sudo (dtrace). If cross‑compiling becomes a time sink, build natively per target in CI.
What’s not covered (yet)
- Unsafe Rust: pointer math,
unsafeblocks, custom allocators. You’ll read the Rustonomicon later; you won’t write unsafe code in these 6 weeks. - Advanced lifetimes and pinning: self‑referential structs,
Pin, projection. Useful but not needed for the artifacts here. - Async internals: building executors/reactors. You’ll use Tokio, not write one.
- Proc‑macros and macro_rules! beyond basics. Powerful, but a separate track.
- Embedded/
no_std, WASM, GUI. Different ecosystems with extra setup. - Deep DataFusion internals (optimizer/planner extensions). You’ll use it, not modify it.
- Complex cross‑compilation matrices/universal2 packaging. You’ll produce practical per‑target builds first; advanced packaging is a follow‑up.
