# Q1: Why are Box and dyn required in Vec<Box<dyn Sale>> ?
dyn — opts into dynamic dispatch
Without dyn , Rust uses static dispatch (generics/monomorphization), meaning only one concrete type per collection. dyn Sale tells the compiler: “I don’t know the exact type at compile time — look it up at runtime via a vtable.”
1 | dyn Sale // fat pointer: (ptr to data, ptr to vtable) |
Box — gives the trait object a known size
dyn Sale is a Dynamically Sized Type (DST) — the compiler doesn’t know its size at compile time, so it can’t be stored directly in a Vec . Box<dyn Sale> fixes this by:
- Allocating each concrete value on the heap
- Storing a fat pointer (data ptr + vtable ptr) — always the same size regardless of the underlying type
1 | Vec<Box<dyn Sale>> |
Vec<Box<dyn Sale>> |
Vec<T: Sale> |
|
|---|---|---|
| Mixed types | Yes | No |
| Dispatch | Runtime (vtable) | Compile-time |
| Heap allocation | Yes (Box) | No |
# Q2: Why do I need dyn Sale rather than just Sale ?
Before Rust 2018, you could write Box<Sale> and it implicitly meant a trait object. The dyn keyword was introduced to remove ambiguity:
| Context | Meaning |
|---|---|
fn foo<T: Sale> |
Trait bound — static dispatch |
Box<Sale> (old) |
Trait object — dynamic dispatch |
Now dyn makes the intent explicit every time. In Rust 2021, writing Box<Sale> without dyn is a hard compile error.
1 | fn foo<T: Sale>(s: T) { ... } // static dispatch |
# Q3: Are traits not types? Can we not store a trait directly?
Two layers:
1. A trait itself is not a type — it’s a contract.
1 | let x: Sale = FullSale(20.0); // ERROR — `Sale` is a trait, not a type |
2. dyn Sale IS a type — but it’s unsized (DST).
1 | let v: Vec<dyn Sale> = ...; // ERROR — size unknown at compile time |
3. A pointer layer gives it a known size.
1 | let v: Vec<Box<dyn Sale>> = ...; // OK — Box is always pointer-sized |
| Storable directly? | Reason | |
|---|---|---|
Sale |
No | Trait (a contract), not a type |
dyn Sale |
No | Unsized type (DST) |
Box<dyn Sale> |
Yes | Fixed-size fat pointer on heap |
&dyn Sale |
Yes | Fixed-size fat pointer (borrowed) |
# Q4: Why is &'a needed in inner: Vec<&'a IdCard> ?
Rule: If a struct contains a reference, you must always annotate its lifetime explicitly. Rust never infers lifetimes in struct definitions.
1 | struct YoungPeople { |
YoungPeople<'a> says: “I borrow IdCard data that lives for at least 'a .” The struct cannot outlive the data it points to.
1 | let ids = new_ids(); // owns IdCard values |
The returned Self in living_in_fooville is YoungPeople<'a> , meaning the new struct still borrows the original IdCard data with the same lifetime — no copying, no new ownership.
# Q5: Why does map take ownership but filter does not?
It comes down to their purpose:
-
filteronly decides keep or discard — it just needs to look at the item, so it borrows (&Item) -
maptransforms the item into something new — it needs to consume it, so it takes by value (Item)
1 | fn filter<P>(self, predicate: P) // closure gets &Self::Item (borrow) |
In the YoungPeople example, self.inner is Vec<&'a IdCard> , so .iter() produces &&'a IdCard :
1 | .filter(|i| i.city == City::Fooville) |
| Closure receives | Why | |
|---|---|---|
filter |
&Item (borrow) |
Only needs to inspect |
map |
Item (by value) |
Needs to transform/replace |
# Q6: Why is &'static str required in const MOCK_DATA: &'static str = include_str!(...) ?
Two separate reasons:
1. const always requires an explicit type annotation
1 | let x = "hello"; // OK — type inferred |
2. include_str! produces &'static str
include_str! embeds the file directly into the binary at compile time. Since that data lives for the entire duration of the program, its lifetime is 'static .
You can also write &str — in a const context, Rust infers the 'static lifetime:
1 | const MOCK_DATA: &str = include_str!("mock-data.csv"); // also valid |
| Requires type? | Lifetime | |
|---|---|---|
let |
No (inferred) | Local scope |
const |
Yes | Always 'static |
static |
Yes | Always 'static |
# Q7: Why can’t Rust infer the type without Vec<_> in collect() ?
collect() can produce many different types — Vec , HashSet , HashMap , String , and more. The compiler can’t choose for you:
1 | let data = MOCK_DATA.split('\n').skip(1).collect(); |
Vec<_> gives the compiler the one hint it needs:
1 | let data: Vec<_> = MOCK_DATA.split('\n').skip(1).collect(); |
Three equivalent ways to write it:
1 | let data: Vec<_> = ...collect(); // you say Vec, Rust infers element |
| Annotation | Container | Element |
|---|---|---|
| none | ambiguous ❌ | — |
Vec<_> |
you decide | Rust infers |
Vec<&str> |
you decide | you decide |
# Q8: What is the difference between filter_map and map ?
map transforms every element, always producing one output per input.
filter_map transforms AND optionally drops elements in one step.
1 | fn map<B, F: FnMut(A) -> B>(self, f: F) // returns B |
Example:
1 | let v = vec!["1", "two", "3", "four"]; |
filter_map is equivalent to .map(f).flatten() — use it instead of .map(...).filter(|x| x.is_some()).map(|x| x.unwrap()) .
| Output per input | Closure returns | |
|---|---|---|
map |
Always 1 | B |
filter_map |
0 or 1 | Option<B> |
# Q9: What does 'a mean in lifetime annotations?
1 | fn longest<'a>(one: &'a str, two: &'a str) -> &'a str { |
'a is a lifetime parameter — a name the compiler uses to track how long references are valid relative to each other.
<'a>in angle brackets declares the lifetime (like<T>declares a type parameter)&'a stron each reference says they are all connected in lifetime
What 'a actually means:
It does NOT mean all references live for exactly the same duration. It means:
“The returned reference is valid for at most the shorter of the two input lifetimes.”
1 | let result; |
Without 'a , the compiler can’t determine how long the return value lives (it could be tied to one or two ), so the function is rejected.
The name 'a is just convention — 'b , 'x , or 'lifetime all work. Single lowercase letters are idiomatic.