Prep: Structs, impl, and const fn — 04r¶
Session: Thu Sep 10, 1h45 · Exercises: 04r_structs_impl · Prep time: ~35 min · Lecture: Building Your Own Types: Structs, impl, const fn, and Enums
What you will build¶
Two of the lecture's types, under plain cargo test: a half-open region of physical memory that knows its extent and is made from a page count, and the Sv39 page table entry of Lecture §4, a newtype around a 64-bit word whose const fn methods pack a physical page number above ten flag bits and pull both back out. One given test already proves the newtype is eight bytes and a #[repr(C)] struct keeps source order. The tests check that your packing matches the hardware layout, round-trips an address, and evaluates at compile time.
Concepts you need¶
- Structs and struct literals — Lecture §2 · Rust for Systems §3
- Methods vs associated functions:
.vs::— Lecture §3 · Rust for Systems §3 - The three receivers and
#[derive(Copy)]— Lecture §3, "The three selves" · Rust for Systems §1 - Newtypes,
#[repr(transparent)], and bit packing — Lecture §4 · Rust for Systems §3 const fnand const contexts — Lecture §5 · Rust for Systems §3#[repr(C)]: layout as a contract — Lecture §6 · Rust for Systems §3
Read before class¶
| What | Time |
|---|---|
| Lecture §2–§4 | 15 min |
| Lecture §5–§6 | 10 min |
| Rust for Systems §3 | 10 min |
Mental model¶
A Unix wait status is one 16-bit word: exit code in bits 15..8, signal number in bits 7..0. Give the word a type and the fields methods:
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct WaitStatus(pub u16);
impl WaitStatus {
pub const fn exited(code: u16) -> WaitStatus { WaitStatus(code << 8) } // no self
pub const fn code(self) -> u16 { self.0 >> 8 } // by value
pub const fn signaled(self) -> bool { self.0 & 0xff != 0 }
}
const OK: WaitStatus = WaitStatus::exited(0); // compile time
Pack by shifting each field into its slot and ORing; unpack by shifting back and ANDing with a mask. WaitStatus::exited(3) goes through the type with :: because no value exists yet; st.code() goes through a dot, and st.signaled() after it still compiles because Copy copies two bytes rather than moving st. Being const fn, OK is a literal in the binary. Every kernel word (entry, process id, saved registers) wears a type this way, so an entry cannot be passed where an address was wanted, and tables of them exist before any code runs.
Check yourself¶
- In
let s = Slot::empty(); s.is_free();, which call is a method and which an associated function?Answer
Slot::empty()is associated: called through the type with::; no value exists yet.s.is_free()is a method: called with a dot on a value, so its first parameter is a form ofself. - A by-value
selfmethod on an eight-byte struct withoutCopyis called twice on one variable. What happens?Answer
The first call moves the value; the second isE0382, use after move. DerivingCopymakes the call copy eight bytes instead:Copymakes assignment stop moving. - What does each need: (a) a static array of 64 process records, correct before the first instruction; (b) a saved-register struct that assembly reads at "base plus 8"?
Answer
(a) Aconst fnto build one record: the linker lays out astatic, and nothing has run to fill it. (b)#[repr(C)], or Rust may reorder fields and offset 8 stops being the stack pointer. It fails silently: the symptom is a garbage jump on a context switch.
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 (github.com/rust-lang/rustlings): the
structsgroup, thenprimitive_typesfor tuples. - 100 Exercises To Learn Rust (rust-exercises.com/100-exercises): chapter 3, "Ticket v1," then chapter 4, "Traits," for
Copyand derive. - Start reading Friday's prep page, Enums and match.