Prep: Errors, and Your First Command — 08r · 10c¶
Session: Fri Sep 18, 1h30 · Exercises: 08r_errors 10c_echo · Prep time: ~40 min · Lecture: Traits, Generics, and the ulib Façade · Buffers, Bytes, and Line-Oriented I/O
What you will build¶
First, a chain of fallible steps over a toy directory: a scan that reports absence with Option, a call that turns absence and an over-long name into failure with your own error enum, steps glued together with ?, and a boundary where the Result collapses into one Unix-style integer — a byte count, or minus an errno number. Second, echo, your first command, against the ulib façade: arguments in as byte slices, bytes out through write_all on fd 1, exit status 0. The tests check that each bad name yields the right error variant (never a panic), and that echo emits byte-exact output for zero, one, several, and empty arguments.
Concepts you need¶
- Absence vs failure:
OptionvsResult— L06 §6.1 · Rust for Systems §4 - Your own error enum;
.ok_or()is the policy line — L06 §6.2 ?, and where aResultbecomes a number — L06 §6.3 · L06 §6.5- A command: argv, fd 1, exit status, one façade — L07 §1 · L06 §7
- Arguments are bytes, not strings — L07 §5 · ulib guide, Portability rules
write_all, never barewrite— L07 §3 · ulib guide, The complete API surface- A separator is not a terminator — L07 §7
Read before class¶
| What | Time |
|---|---|
| L06 §6 Errors as Values (§6.1–6.3, §6.5) | 15 min |
| L06 §7.3 Testing through the seam | 3 min |
L07 §1, §3, §5 (last two paragraphs), §7 (echo paragraph) |
12 min |
| ulib guide: The complete API surface, Portability rules | 10 min |
Mental model¶
Waking a process by pid — not a filesystem, same three layers:
enum ProcError { NoSuchPid, NotSleeping }
fn slot_of(table: &[Proc], pid: u32) -> Option<usize> { /* scan */ }
fn wakeup(table: &mut [Proc], pid: u32) -> Result<(), ProcError> {
let i = slot_of(table, pid).ok_or(ProcError::NoSuchPid)?; // absence becomes failure HERE
if table[i].state != State::Sleeping { return Err(ProcError::NotSleeping); }
table[i].state = State::Runnable;
Ok(())
}
fn sys_wakeup(table: &mut [Proc], pid: u32) -> i64 { // the boundary: one integer
match wakeup(table, pid) { Ok(()) => 0, Err(ProcError::NoSuchPid) => -3, Err(ProcError::NotSleeping) => -22 }
}
The scan states a fact; the middle layer decides, and may use ? only because it returns Result; the edge collapses everything into the one register a user program can receive. A kernel has no exception to throw and nothing to unwind into, so every system call you write from 50k on has this shape.
Check yourself¶
- A directory scan finds no entry for a name.
OptionorResult, and where is "missing means the call failed" decided?Answer
The scan returnsOption— absence is not an error there. The caller that promised to open something converts with.ok_or(SomeVariant); that one line is where the policy lives. - Printing items
x,` (empty),ywith a space **separator**: how many spaces, and what would "a space after every item" give instead? <details><summary>Answer</summary>Two, givingx y— the empty item still counts. "After every item" is a terminator and leaves a trailing space.nitems needn − 1` separators; the newline goes once, after the loop. - Why
write_alland neverwrite, when the tests pass either way?Answer
writemay accept fewer bytes than offered and only says so in its count. The host harness accepts everything, so barewriteis green on your laptop and truncates on rv6 in December.write_allloops until every byte is out.
What "done" looks like¶
oslings run is green, then oslings submit before you leave. Not green? Submit anyway (substantial credit), then finish by Monday 11:59 pm and submit again.
If you finish early¶
Rustlings error_handling and options groups: https://github.com/rust-lang/rustlings. 100 Exercises To Learn Rust, chapter 5 (enums, fallibility, Result, ?): https://rust-exercises.com/100-exercises/. Then start Thursday's prep page on cat.