We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Smart Pointers and Interior Mutability step 22 of 26
Reject stale handles with a generational arena
A slot index can identify the wrong value after that slot is reused. Add generation checks to the supplied arena so old handles stay invalid.
pub fn simulate(ops: Vec<String>) -> Vec<i64>
Arena owns three vectors: slots stores optional values, generations
stores a counter for each slot, and free stores reusable slot indices.
A Handle contains a slot index and the generation at which it was issued.
Keep the supplied command interpreter and repair Handle, Arena::get
and Arena::remove. Each command produces one output:
| Command | Action | Output |
|---|---|---|
insert <v> |
Store the value in the most recently freed slot, or append a new slot. Save the newly issued handle. | Slot index used |
remove <n> |
Remove the value only if issued handle n is valid. |
1 on success, otherwise 0 |
get <n> |
Read through issued handle n. |
Stored value, or -1 for an invalid or unknown handle |
| Unrecognized command or wrong number of tokens | Leave the arena unchanged. | -1 |
Handle numbers and slot indices are different. <n> is the zero-based
position in the list of all handles ever issued. Removing a value does not
remove its handle from that list. A reused slot gets a new handle number.
For example:
commands: ["insert 10", "remove 0", "insert 99", "get 0", "get 1"]
output: [0, 1, 0, -1, 99]
Both insertions use slot 0. Handle 0 must not read or remove the new value; handle 1 can access it.
Validating a handle
A handle is valid only when its slot is in bounds, its generation matches the slot's current generation, and the slot contains a value.
New slots start at generation zero. On a successful removal, empty the
slot, increment its generation and push its index onto free. A failed
removal changes nothing, including the generation and free list. Insertion
into a reused slot preserves its current generation. Use free.pop() so
reuse follows the required last-freed-first order.
Check bounds before indexing either vector. get can return the matching
slot's Option<i64> directly: an empty slot already represents absence.
remove must reject an empty slot to avoid adding it to free twice.
Input details
Commands use case-sensitive names and are split on Unicode whitespace.
Numeric tokens use Rust's standard integer parsing. The supplied interpreter
treats an invalid or out-of-range insert value as 0; an invalid or
out-of-range handle number behaves as an unknown handle. Stored values can
be any i64, including -1, so that output alone cannot distinguish a
stored -1 from a failed lookup. Empty input returns an empty vector.
Exercise scripts do not exhaust a slot's u32 generation counter. For a
reusable library, choose an explicit overflow policy: a wider counter delays
exhaustion but does not eliminate it, and a nonzero integer type does not
prevent generation reuse. One option is to retire a slot before its counter
would wrap, so an old handle cannot become valid again.
Why store handles instead of references?
An arena centralizes ownership. Nodes can store handles to one another, including cycles, without creating reference-counted ownership cycles. Handles remain usable across vector reallocations because they identify slots rather than memory addresses. Access still goes through the arena, and borrowing rules still apply to references obtained from it.
This is one design option alongside Rc, RefCell and other ownership
models. Contiguous storage can improve locality, but arbitrary traversal
can still jump between slots. This implementation has three vectors, each
of which can allocate and grow; it does not promise one allocation or a
universal speedup.
Generations detect reuse within an arena. They do not identify which arena issued a handle: another arena can have the same slot and generation. This exercise uses one arena. A library that needs to reject handles from other arenas must add a separate identity mechanism.
Copying a handle
Derive Copy as well as Clone for Handle. The interpreter copies handles
out of shared references with patterns such as |&h|. The starter's
Clone-only definition produces E0507 at those call sites. Copying these
two integers neither copies the stored value nor transfers its ownership.
Keep the field name generation. gen is reserved in Rust edition 2024;
older code using that name must rename it or use the raw identifier r#gen.
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.