Skip to content

Prep: Control Flow — 01r

Session: Fri Aug 28, 1h30 · Exercises: 01r_control_flow · Prep time: ~25 min · Lecture: Week 1 · Building an Operating System, and Your First Rust

What you will build

Thursday's functions computed one thing and handed it back; Friday's have to decide, repeat, and do arithmetic near the edge of a number. You will write a few small usize functions on your laptop that are the kernel's page-allocator arithmetic with the pointers removed: keep an address inside the board's 128 MiB of RAM (0x8000_0000..0x8800_0000), walk a range one 4 KiB page at a time, and move an address up to a page boundary, both plainly and in a form that reports failure instead of wrapping.

Concepts you need

Read before class

What Time
Week 1 · Functions, and the semicolon that bites, a reread from Thursday, then Friday · 01r Control flow and overflow through Three loops, and half-open ranges 6 min
Week 1 · Overflow: debug panics, release wraps, Say what you mean and Option, in two arms 6 min
Week 1 · For the exam: Overflow in both builds. Not needed today; on Midterm 1 1 min
Week 1 · Problem 2: Debug or release?, on paper, before opening the solution 5 min
Cheatsheet: Constants you must not misremember 3 min

Mental model

Find the largest power of two that fits in a u8:

let mut p: u8 = 1;
let top = loop {
    match p.checked_mul(2) {
        Some(next) => p = next,   // still fits: keep doubling
        None => break p,          // the loop's value: 128
    }
};

loop has no exit except break, so it can hand back a value; checked_mul returns an Option<u8>, and the None arm is the exit. With plain p * 2 the stopping rule vanishes: the eighth doubling is 256, not a u8. Under cargo test that line panics with attempt to multiply with overflow; a release build wraps it to 0, and 0 * 2 never overflows, so the loop never ends.

Check yourself

  1. fn cap(x: u32) -> u32 { if x > 9 { 9 } else { x }; } — what does the compiler say, and why?
    Answererror[E0308]: mismatched types, expected u32, found (). The semicolon discards the if expression's value, so the body is (). Delete it and the if becomes the tail expression.
  2. The allocator's loop tests p + PGSIZE <= stop, not p < stop. What changes when stop is not page-aligned?
    Answerp < stop would hand out a final page that runs past stop. The allocator deals in whole pages, so the test asks "does the whole page fit?", the same half-open convention as KERNBASE..PHYSTOP.
  3. let m = usize::MAX; m + 1 — what happens under oslings run, and under --release? What would you write instead?
    AnswerAs written, neither build runs: with usize::MAX in view, rustc rejects the line, "this arithmetic operation will overflow" (Week 1 · Literals have types too). When m arrives at run time instead: debug, a panic, attempt to add with overflow; release, silently 0. wrapping_add(1) when wrapping is the intent (a counter, a ring index); checked_add(1) when the caller must handle None; saturating_add(1) when clamping is sane. Plain + is for arithmetic you can prove cannot overflow.

What "done" looks like

oslings run is green, then oslings submit before you leave. Not green? Submit anyway — the tests that pass earn their share — then finish it at a make-up session (office hours, on the class network) for three quarters, or anywhere else for half, and submit again.

If you finish early

Work Week 1 · Problem 3: Why loop? and Problem 5: An average that overflows on paper. Rustlings: the 03_if and 04_primitive_types groups, then a peek ahead at 12_options. 100 Exercises To Learn Rust: Chapter 2, "A Basic Calculator" (if/else, panics, while and for, overflow, the wrapping/checked/saturating methods).