// fs.rs:109
```
- A page from the allocator, a 512-byte disk block, the typed-so-far console
buffer
- `ulib::read` (`ulib/src/lib.rs:104`), called from `wc`
(`commands/src/bin/wc.rs:36`)
- In C both are `(char *buf, int n)` and the length is the caller's problem
forever
---
## The Two Kinds
- `&T` — **shared** borrow. Read only. Any number at once.
- `&mut T` — **exclusive** borrow. Read and write, and while it exists it is the
*only* way to reach the value.
"Mutable reference" misleads. What matters is not that you can write through it — it is that nobody else can even look. Read &mut as exclusive.
---
## How to Pass a Value
flowchart TD
A["Pass a value to a function"] --> B{"Does the callee keep it\nafter returning?"}
B -->|yes| C["by value — move\nfn f(v: Vec<usize>)"]
B -->|no| D{"Must it modify it?"}
D -->|yes| E["&mut — exclusive borrow\nfn f(v: &mut [u8])"]
D -->|no| F["& — shared borrow\nfn f(v: &[u8])"]
C --> G["caller's binding is dead"]
E --> H["caller may not touch v\nwhile the borrow lives"]
F --> I["caller may still read v"]
---
## Aliasing XOR Mutation
For any one value at any one moment: either any number of & borrows, or exactly one &mut borrow. Never both.
```rust
let mut page = [0u8; 16];
let a = &mut page; // exclusive borrow #1
let b = &mut page; // exclusive borrow #2 <- error[E0499]
```
Many readers, or one writer.
---
## Why the Kernel Needs This Most
- **Iterator invalidation** — pointer into a list, list reallocates, pointer
aims at freed memory
- **Optimizer hazards** — C must assume same-typed pointers may alias; hence
`restrict`, and its silent miscompiles
- **Concurrent corruption** — two writers, or a reader and a writer, is what
shreds a process table
A kernel is concurrent even on one CPU: an interrupt lands between any two instructions.
---
## Where Rust Cannot Help: the Console
```rust
static mut BUF: [u8; BUF_LEN] = [0; BUF_LEN]; // console.rs:13
static mut HEAD: usize = 0; // next index the consumer will read
static mut TAIL: usize = 0; // next index the producer will write
```
- UART interrupt handler pushes bytes, advances `TAIL`
- A process blocked in `read` pops, advances `HEAD`
- Two agents, shared mutable state, no call relationship
That is *why* these are `static mut` and every access is `unsafe`.
---
## xv6 vs Linux vs rv6
| | xv6 (C) | Linux (C) | rv6 (Rust) |
|---|---|---|---|
| Ownership of memory | comment | comment | type system |
| Use-after-free | anywhere | anywhere; KASAN at run time | only in `unsafe` |
| Lock/data link | comment | `sparse`, `lockdep` | data is *inside* `SpinLock` |
| Forget to unlock | possible | possible | impossible: `Drop` |
| Trusted region | all of it | all of it | the `unsafe` blocks |
---
## Borrows End at Their Last Use
```text
let mut buf = [0u8; 4]; ── buf owned, unborrowed
let view = &buf; ──┐ shared borrow begins
let n = checksum(view); ──┘ last use of `view`: borrow ENDS
fill(&mut buf, 1); ──▶ exclusive borrow: no conflict
```
- Non-lexical lifetimes, Rust 2018
- Pre-2018, `view` stayed borrowed to the closing brace
- Most "why is this rejected?" moments: a borrow used again further down —
**move the later use earlier**
---
## Lifetimes
A **lifetime** is the region of the program over which a reference is valid.
Not seconds. Not something you choose — the borrowed values already decided it.
Three elision rules mean you usually write none:
1. Each elided input reference gets a fresh lifetime
2. With exactly one input lifetime, it goes to every elided output
3. With `&self` / `&mut self`, `self`'s lifetime goes to the outputs
---
## When Elision Fails
```rust
fn longest(a: &[u8], b: &[u8]) -> &[u8] // error[E0106]
```
Two candidate regions, no rule to choose. Name the region:
```rust
fn longest<'a>(a: &'a [u8], b: &'a [u8]) -> &'a [u8]
```
Read it as: for some region 'a, give me two references valid at least that long, and I return one also valid that long.
---
## Structs That Hold References
```rust
pub struct Args<'a> { // ulib/src/lib.rs:63
argv: &'a [&'a [u8]],
}
```
- `Args` does **not** own the command line
- `exec` pushed those bytes onto the new process's stack
- `'a` says: an `Args` may not outlive that memory
---
## The Destination: `SpinLockGuard`
```rust
pub fn lock(&self) -> SpinLockGuard<'_, T> { // spinlock.rs:22
/* spin */ SpinLockGuard { lock: self }
}
pub struct SpinLockGuard<'a, T> { // spinlock.rs:54
lock: &'a SpinLock,
}
impl Drop for SpinLockGuard<'_, T> { // spinlock.rs:71
fn drop(&mut self) { self.lock.unlock(); }
}
```
---
## The Safety Argument
flowchart LR
L["SpinLock<T>\nlives in a static"] -->|"lock() borrows &self for 'a"| G["SpinLockGuard<'a, T>"]
G -->|"DerefMut yields &mut T"| D["the protected data"]
G -.->|"Drop::drop → unlock()"| U["released at the guard's\nclosing brace"]
D -.->|"cannot outlive"| G
G -.->|"cannot outlive"| L
"Only touch this data while you hold the lock" stops being a rule people
remember and becomes one the compiler enforces.
---
## The Four Errors You Will Hit
| Code | What it really means | Usual fix |
|---|---|---|
| **E0382** | You gave the value away, then named it again | borrow instead; or return it; or `.clone()` |
| **E0499** | Two writers to one value at one time | let the first `&mut` end first |
| **E0502** | A reader and a writer overlap | read the value out, not a reference to it |
| **E0106** | Signature does not say which input the result borrows from | add `<'a>`, or return an owned value |
Ask: *who owns this, and who is looking at it now?*
---
## Where the Rules Run Out
```rust
/// Raw pointer to process slot `i`. Lets other modules reach the table without
/// creating references into a `static mut`. // proc.rs:70
pub unsafe fn proc_at(i: usize) -> *mut Proc {
ptr::addr_of_mut!(PROCS[i])
}
```
unsafe does not turn off the borrow checker. It enables five extra abilities. Ownership and borrow rules still apply inside the block.
---
## This Week's Exercises
- **`02r_ownership`** (Thursday, September 3) — a page allocator built from nothing but a `Vec`
and move semantics. "Handed out is not free" enforced by signatures.
- **`03r_borrowing`** (Friday, September 4) — `&`, `&mut`, slices, and a `Guard<'a>` holding a
`&'a mut u64`.
```bash
oslings run 02r_ownership
oslings watch # re-runs on every save
oslings hint
```
---
## Summary
1. **One owner, always** — transferred, never shared
2. **A move** is 3 words of stack plus a compile-time death sentence
3. **`Copy` excludes `Drop`** — or release would run once per copy
4. **`Drop` is where `free()` went** — deterministic, reverse declaration order
5. **`&` is shared, `&mut` is exclusive** — read `&mut` as *exclusive*
6. **Aliasing XOR mutation** — many readers or one writer, never both
7. **Lifetimes** state a relationship the compiler checks at every call site
8. **The guard pattern** turns a locking convention into a checked fact