01 A descriptor is the lowest free slot in a table.
a descriptor is the lowest free slot · point at the open after close(4) returning 4, not 6
Every program in examples/src/bin/ beside the output it printed, and every
file in examples/broken/ beside what rustc said about it. Each colored,
numbered section of code sits in the same row as the output it produced. Click an output line to light
up the println! that printed it, or click a println! to find its output.
Click a section's number to copy a link straight to that section, to paste where students will
follow it. Press i on a program to edit it and run it. On a phone the two columns do not fit, so a
Code/Output switch shows one at a time.
| Program | The one idea | The line to point at | Sections | |
|---|---|---|---|---|
| 01 | fd_numbers | a descriptor is the lowest free slot | the open after close(4) returning 4, not 6 | 5 |
| 02 | short_reads | a short read is normal; only 0 ends it | buf[3..6] still saying "lo\n" | 5 |
| 03 | buffer_size_counts_calls | the buffer's size sets how many times you trap | 2,048 reads beside 1,048,576, and 8,192 on rv6 either way | 4 |
| 04 | read_loop_tr | listen to both counts, read's and write's | write_all(dst, &buf[..n])? beside the bare write that lost 10 bytes | 5 |
| 05 | exit_status | a later success does not erase a failure | status = 1; // only ever set, never cleared | 6 |
| 06 | bytes_chars_str | len counts bytes, not characters | "año".len() = 4 beside .chars().count() = 3 | 7 |
| 07 | state_across_chunks | state the caller keeps survives the end of a read | after_cr: &mut bool, and the two <- wrong rows | 5 |
| 08 | lines_in_a_fixed_buffer | an unfinished line slides to the front | self.buf.copy_within(self.start..self.len, 0), then read(fd, &mut buf[6..]) -> 10 | 5 |
| 09 | while_let | call, match, and stop at the first miss | while let Some(line) = lines.next_line(), and Some("") for a blank line | 5 |
| 10 | fenceposts | the last start is len - k, and only ..= reaches it | (0..3).contains(&3) = false for log in syslog | 6 |
| 11 | stop_early | what a program does not read costs nothing | 1,024 bytes read out of 10,000,000,000 | 4 |
| Error | What rustc says | The fix |
|---|---|---|
| E0277 | Lines<'_> is not an iterator | while let Some(line) = lines.next_line() |
| E0308 | mismatched types expected u8, found char | b'\t', a byte literal |
| E0499 | cannot borrow lines as mutable more than once at a time | finish with each line first, or copy out what you need |
| E0502 | cannot borrow buf[_] as immutable because it is also borrowed as mutable | read through the line, or after the last use of lines |
| E0506 | cannot assign to buf[_] because it is borrowed | edit a copy, or write after the last use of lines |
| E0599 | no method named chars found for reference &[u8] in the current scope | stay in bytes, or from_utf8 first |
| Space j ↓ → | next section (dims the rest) |
| Shift+Space k ↑ ← | previous section |
| 0–9 | jump to that section |
| click | pin an output line and the code that printed it (click again to unpin) |
| click a number | copy a link to that section, or to the whole example from the number beside its title |
| l | copy a link to the pinned line, else the section in focus, else this example |
| Esc | unpin, then leave section focus, and drop it from the link so a reload does not bring it back |
| n p | next or previous example |
| r | next run, for programs run more than once |
| e | open or close the explanation (broken files) or the build warnings |
| + - | larger or smaller text (remembered) |
| i | edit this program and run it |
| ⌘/Ctrl+↵ | run what you have edited |
| c | compare: what you changed in the code, and what changed in the output (press Esc first if you are typing) |
| o | on a narrow screen, switch between the code and the output column |
a descriptor is the lowest free slot · point at the open after close(4) returning 4, not 6
a short read is normal; only 0 ends it · point at buf[3..6] still saying "lo\n"
the buffer's size sets how many times you trap · point at 2,048 reads beside 1,048,576, and 8,192 on rv6 either way
tr a-z A-Z on a stingy kernel.listen to both counts, read's and write's · point at write_all(dst, &buf[..n])? beside the bare write that lost 10 bytes
a later success does not erase a failure · point at status = 1; // only ever set, never cleared
len counts bytes. A character may be several of them.len counts bytes, not characters · point at "año".len() = 4 beside .chars().count() = 3
state the caller keeps survives the end of a read · point at after_cr: &mut bool, and the two <- wrong rows
an unfinished line slides to the front · point at self.buf.copy_within(self.start..self.len, 0), then read(fd, &mut buf[6..]) -> 10
while let: call, match, and stop at the first miss.call, match, and stop at the first miss · point at while let Some(line) = lines.next_line(), and Some("") for a blank line
the last start is len - k, and only ..= reaches it · point at (0..3).contains(&3) = false for log in syslog
what a program does not read costs nothing · point at 1,024 bytes read out of 10,000,000,000
Lines<'_> is not an iteratorfor line in lines · fix: while let Some(line) = lines.next_line()
for calls Iterator::next, and Lines has no such method, only next_line. It cannot have one: next(&mut self) -> Option<Self::Item> gives Item no way to borrow from this one call, and every line is a slice of the buffer that the NEXT call may overwrite.
while let Some(line) = lines.next_line() { .. } -- call, match, and stop at the first None.loop { match lines.next_line() { Some(line) => .., None => break } }.u8, found chara u8 compared with the char '\t' · fix: b'\t', a byte literal
Walking a &[u8] gives bytes. '\t' is a char: four bytes, one Unicode scalar value. A u8 and a char never compare, even when the byte is ASCII, because Rust will not guess an encoding for you.
b == b'\t' -- a byte literal is a u8.b as char == '\t' also compiles, and reads each byte as a whole character: fine for ASCII, wrong for both bytes of ñ.lines as mutable more than once at a timea line held across the next next_line · fix: finish with each line first, or copy out what you need
A line is a slice of the buffer lines holds on loan, and the next call to next_line may slide or refill that buffer. So first keeps lines borrowed for as long as first is used, and the second call needs it again. C compiles this and prints whatever the refill left there.
next_line again. while let does that for you.buf[_] as immutable because it is also borrowed as mutablereading buf[0] while lines has the buffer · fix: read through the line, or after the last use of lines
Lines::new(&mut buf) lends the WHOLE array to lines for as long as lines is in use, and it is used again on the next line. Reading buf[0] in between would read bytes that next_line may be moving.
lines: the line it returned is the part of buf you may look at, and it is a slice of the same bytes.lines first. After its last use, buf is yours again, and buf[0] compiles.buf[_] because it is borrowedwriting buf[5] while lines has the buffer · fix: edit a copy, or write after the last use of lines
The same loan as e0502, written to instead of read. lines holds buf until its last use, and next_line trusts that the bytes it left in the buffer are still there. A write in between would change a line that lines has not handed out yet.
lines first. After its last use, buf is yours to write again.chars found for reference &[u8] in the current scope.chars() on a &[u8] · fix: stay in bytes, or from_utf8 first
chars decodes UTF-8, so it lives on str, whose bytes are PROMISED to be valid UTF-8. A &[u8] promises nothing: it might be half of a ñ, or a disk block. There is no char to hand back until somebody checks.
line.first().is_some_and(|b| b.is_ascii_uppercase()). Every command this week works this way.std::str::from_utf8(line) returns a Result, and the &str inside an Ok has .chars().