01 One function, three containers.
one function, three containers · point at count_free(&arr) and count_free(&v): the same &
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 | array_slice_vec | one function, three containers | count_free(&arr) and count_free(&v): the same & | 6 |
| 02 | iter_mut_and_enumerate | *c = None writes into the array | the * | 6 |
| 03 | slot_search | an index, or nothing — never −1 | position(..) returning Option<usize> | 6 |
| 04 | adapters_and_closures | three words, three types | .flatten().copied().collect::<Vec<u16>>() | 9 |
| 05 | trait_required_default | the default was never written by any implementer | puts has a body in the trait; no impl mentions it | 6 |
| 06 | generics_and_bounds | one body, three names | type_name::<U>() printed from inside the generic | 6 |
| 07 | static_vs_dyn | a fat pointer is two words | size_of::<&dyn Uart>() = 16 | 7 |
| 08 | option_vs_result | absence is a fact, failure is a decision | first_free(free).ok_or(AllocError::OutOfFrames) | 6 |
| 09 | error_enum_and_question_mark | each ? is a different exit | the three ?s in load | 4 |
| 10 | errno_boundary | the collapse happens in exactly one place | the match in sys_read | 6 |
| 11 | bytes_vs_strings | a string is bytes plus a checked promise | str::from_utf8(..) returns a Result | 7 |
| 12 | argv_and_write_all | the slice is re-pointed, not copied | buf = &buf[n..] | 6 |
| 13 | dispatch_at_run_time | who names the type: the call site, or the input | fn open(&mut self, k: Kind) -> &mut dyn Uart | 6 |
| 14 | generic_struct_and_guard | one definition, a type for every T | impl<T> Lock<T> beside impl<T: Uart> Lock<T> | 6 |
| Error | What rustc says | The fix |
|---|---|---|
| E0038 | the trait Sink is not dyn compatible | where Self: Sized, or take a slice, or go generic |
| E0046 | not all trait items implemented, missing: put | supply put; delete the override |
| E0107 | missing generics for struct FdTable | name it, go generic too, &'a mut dyn Sink, or a tag |
| E0277 | the ? operator can only be used in a function that returns Result or Option (or another type that implements FromResidual) | match at the boundary; ? only below it |
| E0308 | mismatched types expected Result<Device, DevError>, found Option<Device> | .ok_or(e), or the two-arm match |
| E0392 | type parameter T is never used | hold one in a field, PhantomData<T>, or drop the parameter |
| E0506 | cannot assign to table[_] because it is borrowed | iter_mut and *slot = …, or an index loop |
| E0596 | cannot borrow *table as mutable, as it is behind a & reference | change the parameter, not the loop |
| E0599 | no method named put found for mutable reference &mut S in the current scope | add S: Sink |
| 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 |
one function, three containers · point at count_free(&arr) and count_free(&v): the same &
iter() lends, iter_mut() lends exclusively.*c = None writes into the array · point at the *
an index, or nothing — never −1 · point at position(..) returning Option<usize>
three words, three types · point at .flatten().copied().collect::<Vec<u16>>()
the default was never written by any implementer · point at puts has a body in the trait; no impl mentions it
one body, three names · point at type_name::<U>() printed from inside the generic
dyn: one copy of the code, and a fat pointer to find the method.a fat pointer is two words · point at size_of::<&dyn Uart>() = 16
absence is a fact, failure is a decision · point at first_free(free).ok_or(AllocError::OutOfFrames)
? that carries it.each ? is a different exit · point at the three ?s in load
Result becomes a number.the collapse happens in exactly one place · point at the match in sys_read
a string is bytes plus a checked promise · point at str::from_utf8(..) returns a Result
the slice is re-pointed, not copied · point at buf = &buf[n..]
who names the type: the call site, or the input · point at fn open(&mut self, k: Kind) -> &mut dyn Uart
one definition, a type for every T · point at impl<T> Lock<T> beside impl<T: Uart> Lock<T>
Sink is not dyn compatiblea generic method, then &mut dyn Trait · fix: where Self: Sized, or take a slice, or go generic
A vtable has one slot per method. put_each is GENERIC over I, so it would need one slot per type it is ever called with -- not a number the compiler knows. fn new() -> Self and methods without self fail for the same reason. &mut dyn Sink therefore cannot exist for this trait.
where Self: Sized on put_each -- it exists for concrete types and vanishes from the vtable.put_each(&mut self, items: &[&[u8]]).fn banner<S: Sink>(s: &mut S) -- go generic and never need dyn.putoverrode the default, skipped the required · fix: supply put; delete the override
put_line has a body in the trait, so it is a DEFAULT: optional. put has none, so it is REQUIRED. This impl wrote the optional one and skipped the required one -- and the default is BUILT ON put, so nothing works without it. The compiler names exactly what is missing.
fn put(&mut self, b: &[u8]) and delete the override; the default put_line then works for free.put is still required either way.FdTablea generic struct used without its type argument · fix: name it, go generic too, &'a mut dyn Sink, or a tag
07 asserts that [&mut U; 3] cannot hold three sinks, because U is ONE type. Here is the proof, twice: FdTable<U> is not a type until you name U, so every layer that touches one must name it too -- and once you have written FdTable<Console>, the assignment in main is error[E0308].
fn banner<S: Sink>(t: &mut FdTable<S>) -- go generic too, and hand the choice one layer up. Every layer up pays the same tax.struct FdTable<'a> { out: &'a mut dyn Sink } -- one field type, any sink, re-pointed at run time. Program 13 does this.enum Kind { Tty, File, Null } and a match -- rv6's FileKind.? operator can only be used in a function that returns Result or Option (or another type that implements FromResidual)? in a function returning i64 · fix: match at the boundary; ? only below it
? is a return Err(e) in disguise, and this function returns i64 -- there is no Err to return. This IS the boundary: the place where ? stops and match starts, because past here the caller only understands a number. 10c's fn run(args: Args) -> i32 is the same wall.
match reg.lookup(name) { Ok(d) => .., Err(e) => -errno_for(e) } -- collapse to a number HERE, exactly once.? down into a helper that returns Result; match its result here.Result<Device, DevError>, found Option<Device>returned find's Option from lookup · fix: .ok_or(e), or the two-arm match
find reports ABSENCE: a name that is not there, nothing wrong. lookup must report FAILURE, and nobody said what absence MEANS here. The two enums do not convert themselves -- that decision is yours, and it is one line.
self.find(name).ok_or(DevError::NoSuchDevice) -- Some -> Ok, None -> Err(the one you chose). That choice is the policy.match self.find(name) { Some(d) => Ok(d), None => Err(..) } -- the same thing, longhand.T is never useda generic struct that never holds its T · fix: hold one in a field, PhantomData<T>, or drop the parameter
A generic FUNCTION names its parameter in the signature; a generic STRUCT has to name it in a FIELD. This lock declares a T and then guards only a bool, so Lock<VecSink> and Lock<u64> would be the same bytes.
struct Lock<T> { held: bool, data: T }, which is program 14, and rv6's SpinLock<T> with UnsafeCell<T>.PhantomData<T> for a T you want in the type and not in the bytes: units, a state machine, a device tag.struct Lock { held: bool } is week03's program 07.table[_] because it is borrowedassigning table[i] inside table.iter() · fix: iter_mut and *slot = …, or an index loop
The for loop's iterator holds a SHARED borrow of the whole table until the loop ends. table[i] = None writes into it while that borrow is alive. (On a Vec the same line is E0502 -- indexing a Vec is a method call.) This is exit() closing every descriptor; it is also 06r's alloc_slot.
for (i, slot) in table.iter_mut().enumerate() { *slot = None; } -- one &mut per slot, handed out in turn, and the write goes through it.for i in 0..table.len() -- no iterator, so no borrow is held.(A return right after the write is accepted: the borrow checker can see the iterator is never used again. A loop that CONTINUES is not.)
*table as mutable, as it is behind a & referenceiter_mut on a &[T] parameter · fix: change the parameter, not the loop
The loop is fine. The PARAMETER is wrong. &[T] bought the right to read; iter_mut needs the right to write, and no loop can add that. The caller may well have passed &mut table -- the signature threw the mut away.
table: &mut [Option<u32>] -- the signature says what the body does.iter() and return what you found.put found for mutable reference &mut S in the current scopea trait method on an unbounded type parameter · fix: add S: Sink
S is SOME type -- the body may assume only what the bound says, and there is no bound. This is not a missing use; rustc's help text says the fix: "perhaps you need to restrict type parameter S with it: S: Sink"
fn log_all<S: Sink>(sink: &mut S, ..) -- the bound is the contract in both directions: what the body may call, what the caller must prove.sink: &mut impl Sink (same thing), or &mut dyn Sink (one copy).