We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Performance and Data Layout step 18 of 20
Reserve output space and report iterator size hints
Avoid repeated output-buffer growth in two tasks: concatenating rows and collecting values from a run-length iterator. The starter produces the right values, but it does not meet the size-hint and allocation requirements.
1. Concatenate the rows
pub fn flatten_rows(rows: &[Vec<i32>]) -> Vec<i32>
Return every value in row order, preserving duplicates and negative values. Rows may have different lengths or be empty. An empty input, or an input of only empty rows, produces an empty vector. Leave the input unchanged.
For example, [[2, -1], [], [2, 7]] becomes [2, -1, 2, 7].
Reserve enough capacity for the sum of the row lengths, then append each row.
extend_from_slice is useful here. rows.concat() is another option.
The total length fits in usize and the output fits in memory.
The starter's rows.iter().flatten().copied().collect() has the right output,
but does not reserve the full combined length on the tested toolchain. The
number of rows alone cannot tell Flatten how many values the rows contain.
It does not scan all those row lengths just to report its initial size hint.
2. Report the remaining run length
Complete the Iterator implementation for RunLength, constructed by:
pub fn run_length(runs: Vec<(i32, usize)>) -> RunLength
Each pair is (value, count). For example, [(7, 3), (9, 2)] yields
7, 7, 7, 9, 9. Skip zero-count runs wherever they appear. An empty list or
all-zero counts yields no values. Values may be any i32; the sum of counts
fits in usize, and the decoded output fits in memory.
The supplied next method already tracks remaining. Implement size_hint
so it returns (remaining, Some(remaining)) without consuming any values.
It must stay exact after every successful next call and return
(0, Some(0)) after exhaustion.
For [(7, 3), (9, 2)], the initial hint is (5, Some(5)). After taking three
values it is (2, Some(2)), and collecting the rest gives [9, 9].
What a size hint promises
Iterator::size_hint returns (lower, upper). The lower bound cannot exceed
the number of remaining values; Some(upper) cannot be smaller than that
number. None means no upper bound is supplied, not that the iterator is
necessarily infinite. The default (0, None) is valid but gives little
information for reserving an output buffer.
A loose bound is allowed by the trait. This exercise deliberately requires
exact bounds because the decoder already knows the remaining count. A wrong
bound is a logic bug, but safe iterator consumers must not rely on it for
memory safety. Returning an exact hint does not automatically implement
ExactSizeIterator; that separate, optional trait exposes len() and is
not required here.
This complete example shows that the hint describes values still to come:
fn main() {
let mut values = [4, 8, 12].into_iter();
assert_eq!(values.size_hint(), (3, Some(3)));
assert_eq!(values.next(), Some(4));
assert_eq!(values.size_hint(), (2, Some(2)));
assert_eq!(values.collect::<Vec<_>>(), vec![8, 12]);
}
Allocation checks
The harness allows at most one call to alloc, alloc_zeroed or realloc
during flatten_rows, and at most one while collecting the decoder's
remaining values. It reads the decoder hint before and after attempting to
take three values. If fewer than three exist, the iterator is exhausted.
The large fixture flattens 2000 rows of 500 values and decodes 100,000 values.
A useful size hint lets a vector reserve space before filling. The precise
strategies used by collect and extend are implementation details; they
are not universally a call to with_capacity(size_hint().0). A vector also
has no guaranteed doubling policy, and growing its allocation does not
necessarily copy the existing data.
The allocation counter checks this exercise on the current toolchain. It is not a timing measurement or a portable promise that every allocation call will occur: Rust permits the optimizer to eliminate allocations.
Iterator syntax alone does not determine performance. An adapter with an inexact length can still compile into an efficient loop, especially when no output collection is needed. Reserve or precompute a length when it helps the actual workload; benchmark before assuming a manual loop is faster.
Your solution must compile, match the values and exact hints, meet the allocation limits and pass Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.