Skip to content

Prep: Ownership — 02r

Session: Thursday Sep 3, 1 h 45 min · Exercises: 02r_ownership · Prep time: ~25 min · Lecture: Ownership, Borrowing, and Lifetimes

What you will build

A physical page allocator modeled by hand from a Vec<usize> of free page numbers and move semantics. There is no & yet, so each piece takes the whole free list by value and hands it back in a tuple: build the list at "boot," hand a page out like kalloc, take one back like kfree, and return a sentinel page number that can never be real when the list runs dry. One small piece shows that a String given to a function must be handed back before the caller can use it. The given tests check that no page is handed out twice, that a returned page becomes free again, and that page numbers survive being passed around while the list does not.

Concepts you need

Read before class

What Time
Lecture §§1–3 (the bug, ownership, moves, Copy, drop) 12 min
Lecture §7, the E0382 row and the reading habits 3 min
Guide §1 Ownership and moves 6 min
Guide §5, "Three ways to hold a run of values" 3 min

Mental model

A function that takes an owned value, changes it, and gives it back, plus a number riding along:

fn stamp(mut msg: String, n: u32) -> (String, u32) {
    msg.push('!');                 // legal only because the parameter says `mut`
    (msg, n + 1)                   // hand the String back
}

let msg = String::from("boot");
let n = 7;
let (msg, total) = stamp(msg, n);  // moved in, moved back out into a new `msg` (shadowing)
assert_eq!(n, 7);                  // `u32` is Copy: the original survives
assert_eq!((msg.as_str(), total), ("boot!", 8));

While stamp runs it is the only owner of that String. The kernel's allocator needs that: while "hand out a page" runs it owns the whole free list, so the page it returns cannot still be on it. Ownership is that invariant, checked by the compiler.

Check yourself

  1. After let b = a; where a: String, what happens at run time, and what at compile time?
    AnswerRun time: three machine words (pointer, length, capacity) are copied; the heap buffer is untouched. Compile time: a is marked dead; naming it again is E0382, a use-after-free caught early.
  2. Which of these are Copy: usize, bool, String, Vec<usize>, (usize, usize)?
    Answerusize, bool, and the tuple; not String or Vec<usize>. The line is not size but resources: a type with cleanup (Drop) cannot be Copy, or cleanup would run once per copy.
  3. A function takes a Vec<usize> by value and calls .push on it; the caller needs the list afterward. Without &, what must it do?
    AnswerWrite mut before the parameter name (or .push is E0596), and return the Vec, usually in a tuple. The caller rebinds with let (list, x) = f(list);, shadowing the dead list.

What "done" looks like

oslings run is green, then oslings submit before you leave. Not green? Submit anyway (substantial credit), then finish by Thursday 11:59 pm and submit again.

If you finish early

Rustlings: 06_move_semantics, then 05_vecs and the tuple exercises in 04_primitive_types. 100 Exercises To Learn Rust: chapter 3, the Ownership, Stack, Heap, and Destructors sections; chapter 4, Copy and Drop. Then start Friday's prep page on borrowing, the fix for every "return it so the caller keeps it" line.