The Rust Mindset
Before Project 1, spend ten minutes on the one idea the whole language hangs from. If ownership clicks, the borrow checker stops feeling like a bouncer and starts feeling like a pair programmer who has already spotted the bug you were about to write. This page is that click.
The one rule
Section titled “The one rule”Every value in Rust has exactly one owner — the variable responsible for cleaning it up. When the owner goes out of scope, the value is freed. That’s it. Everything else — moves, borrows, lifetimes — is machinery to keep that one rule true while still letting you write useful programs.
{ let s = String::from("hi"); // s owns the heap buffer ... } // s goes out of scope → buffer freed, automaticallyNo garbage collector, no manual free(). The compiler inserts the cleanup because it knows who the
owner is. The cost it charges for that: you have to make ownership unambiguous, and that’s what the
borrow checker is checking.
Move, don’t copy
Section titled “Move, don’t copy”Assigning or passing a heap value moves ownership; the old binding is invalidated:
let a = String::from("hi"); let b = a; // ownership MOVES from a to b println!("{a}"); // ❌ compile error: a no longer owns anythingThis looks hostile until you see what it prevents: if both a and b “owned” the buffer, both would
try to free it at scope end — a double-free, a classic C bug. Rust makes that un-writable. If you
genuinely want two independent copies, you ask: let b = a.clone(); — explicit, visible, and you can
see the cost in the code.
Borrowing: use without owning
Section titled “Borrowing: use without owning”You rarely want to give away ownership just to read something. So you borrow a reference instead:
fn length(s: &String) -> usize { s.len() } // borrows, doesn't take ownership
let s = String::from("hello"); let n = length(&s); // lend s println!("{s}: {n}"); // ✓ s is still oursThe borrow checker enforces two rules on references, and they are the entire game:
Either one mutable borrow (
&mut), or any number of shared borrows (&) — never both at once.
let mut v = vec![1, 2, 3]; let r = &v; // shared borrow v.push(4); // ❌ can't mutate while r is borrowing it println!("{r:?}");Try it: dial the number of shared borrows up and down and flip the mutable borrow on
and off. The only combination the compiler rejects is “a &mut and one-or-more & at the
same time” — that’s “shared XOR mutable” made tangible.
Borrow-checker rule visualizer
&xlet mut x = 5; let r1 = &x; // shared borrow #1 let m = &mut x; // mutable borrowThis is the shared XOR mutable rule: you may have either any number of shared borrows
& or exactly one mutable borrow &mut — never both at once. Mixing them is the only invalid combination, and it's exactly what prevents data races and dangling references.Under the hood — what that rule actually buys
Section titled “Under the hood — what that rule actually buys”That single “shared XOR mutable” rule is not bureaucracy; it’s the thing that makes Rust’s promises true:
- No dangling pointers. A reference can’t outlive the value it points to (the compiler tracks this with lifetimes), so you can never read freed memory.
- No data races, at compile time. A data race needs two threads, shared data, and at least one
writer, with no synchronization. “Shared XOR mutable” makes that combination impossible to express —
which is why Rust calls it fearless concurrency. You’ll feel this directly in
kvlite.
The feedback loop is the lesson
Section titled “The feedback loop is the lesson”Here’s the mental shift that makes Rust learnable: the compiler is not grading you, it’s teaching
you. rustc’s errors are unusually specific — they name the value, the borrow, and often the fix. The
fastest way to learn is to write code you think is right, let the compiler object, and read the
objection. That loop only works if you’re building, which is the whole premise of this playbook.
The thread
Section titled “The thread”What does building this force you to understand — and what is Rust’s compiler protecting you from? From page one, the answer is the same shape: the compiler is keeping the “one owner” rule true so memory is freed exactly once and never used after, and keeping “shared XOR mutable” true so concurrency can’t race. You satisfy it by making ownership explicit. That’s the entire tax — and in return you get C-level speed with none of C’s memory bugs. Now go build something: Project 1 · logwise →
Check your understanding
Section titled “Check your understanding”- State Rust’s single ownership rule in one sentence. What does the compiler do when an owner goes out of scope?
- Why does
let b = a;(whereais aString) makeaunusable afterward — and what real bug is that preventing? - Why does an
i32copy on assignment while aStringmoves? What trait marks the difference? - State the borrow checker’s “shared XOR mutable” rule. Name the two guarantees it buys you.
- The page claims the compiler is “teaching, not grading.” What concrete working habit does that imply for learning Rust fast?
Show answers
- Every value has exactly one owner, the variable responsible for cleaning it up; when the owner
goes out of scope, Rust automatically frees the value (no GC, no manual
free). - Assigning a non-
Copyheap value moves ownership tob, soano longer owns anything. It prevents a double-free: if bothaandbowned the buffer, both would try to free it at scope end. i32is small, stack-only, and implements theCopytrait, so duplicating its bytes is trivially safe;Stringowns a heap buffer and is notCopy, because a byte-copy would create two owners of one buffer (so it moves instead).- Either one mutable borrow (
&mut) or any number of shared borrows (&), never both at once. It buys no dangling pointers (a reference can’t outlive its value) and no data races at compile time (fearless concurrency). - Write the code you think is right and read the compiler’s error instead of trying to satisfy the
checker up front — learn in the fast feedback loop with
rustc, which only runs if you’re actually building and typing.