We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership I: Moves, Copy, Clone, Drop step 2 of 20
Stack, heap, and what a variable actually holds
Report the shape of five types, measured in machine words.
pub fn layout_report() -> Vec<usize>
Nearly every ownership confusion is really a mental-model confusion about where
the bytes live. Once you can draw a String as three words on the stack
pointing at a heap buffer, let s2 = s1; stops being a rule and becomes a
picture with an obvious problem in it.
The picture
stack heap
┌───────────────┐
│ ptr ────────┼──────────▶ ┌───┬───┬───┬───┬───┐
│ len = 5 │ │ h │ e │ l │ l │ o │
│ cap = 8 │ └───┴───┴───┴───┴───┘
└───────────────┘ (3 bytes spare)
a String
A String value is those three words. The characters are somewhere else
entirely, and the String merely knows where. Vec<T> has exactly the same
shape. Box<T> is one word — a pointer with no length and no capacity, because
a Box holds exactly one T. And &str is two words: a pointer and a
length, with no capacity, because a &str does not own its bytes and therefore
has no say in how much room they were given.
Now re-read rule 2 of the last problem. When you assign a String, the three
stack words are copied. Both variables would point at the same heap buffer, and
both would eventually try to free it. Rust’s answer — invalidate the source — is
the cheapest possible fix: no allocation, no reference count, no runtime check.
Just a note in the compiler’s head that one of the two names is now dead.
The task
Return a Vec<usize> with exactly five entries, in this order:
| index | value |
|---|---|
| 0 |
size_of::<String>() divided by size_of::<usize>() |
| 1 |
size_of::<Vec<u8>>() divided by size_of::<usize>() |
| 2 |
size_of::<Box<u8>>() divided by size_of::<usize>() |
| 3 |
size_of::<&str>() divided by size_of::<usize>() |
| 4 |
1 if Option<Box<u8>> is the same size as Box<u8>, else 0 |
Every entry is a ratio or an equality — never an absolute byte count. That
is deliberate. Your code runs on your machine, and a 64-bit target would give
24 bytes for a String while a 32-bit target gives 12. Measured in words,
both give 3. Writing size assertions in words rather than bytes is a habit
worth having: it is the difference between a test that documents a design and a
test that documents your laptop.
Entry 4: the niche
Option<T> has to record which of two variants it is, so you would expect it to
cost one extra word. For Option<Box<u8>> it costs nothing at all.
The reason is that Box is never null. That makes the all-zeros bit pattern an
invalid value for a Box — a niche — and the compiler is free to spend it
on None. Some(b) is the pointer; None is all zeros. Same size, no tag.
This is also true for Option<&T>, Option<Rc<T>>, Option<NonZeroU32> and
several others, and it is why Option::take (which you will meet later in this
track) is genuinely free rather than merely cheap.
::: question If the niche trick works for Option<Box<u8>>, does Option<u8> also cost one byte?
No — it costs two.
u8 has 256 valid bit patterns and uses all of them. There is no invalid
pattern left over for None to occupy, so the compiler falls back to a
separate discriminant byte, and alignment does the rest:
size_of::<Option<u8>>() == 2.
Niches only exist where a type has values it is not allowed to hold. bool
(two valid patterns out of 256) has a huge niche; char has one; &T, Box<T>
and NonZero* have exactly one. Plain integers have none. It is a nice example
of a validity invariant paying for itself in layout.
:::
Two practical notes
size_of has been in the prelude since Rust 1.80, so you do not need
use std::mem::size_of;. Writing the import anyway still compiles and still
passes this problem’s gate — rustc’s redundant_imports lint is allow-by-default
so nothing will tell you — but it is noise, and reviewers will flag it.
Turning a bool into a usize: usize::from(flag) is the direct way and says
what it means. flag as usize also works. Prefer the first; as is a blunt
instrument you will want to reserve for cases where nothing safer exists.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.