We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Smart Pointers and Interior Mutability step 20 of 26
OnceCell: write-once interior mutability
Cache a sorted copy of the input so that several queries can reuse it. This is memoization: storing a computation's result instead of repeating it.
pub fn cached_stats(data: Vec<i64>, queries: Vec<String>) -> (Vec<i64>, usize)
Fix the starter's Stats type using OnceCell<Vec<i64>> for the sorted
cache. Keep its query methods taking &self and use Cell<usize> to count
how often the cache initializer runs.
Return the answers in query order and the sort count. Query names are case-sensitive:
| Query | Answer for nonempty data | Initializes the cache? |
|---|---|---|
median |
sorted[len / 2], the upper middle element for even lengths |
Yes |
p90 |
sorted[min(len * 9 / 10, len - 1)] using integer division |
Yes |
min |
sorted[0] |
Yes |
count |
The input length | No |
| Anything else | -1 |
No |
For empty data, the four recognized queries return 0; unknown queries
still return -1. Calling median, p90, or min initializes the
cache even when the input is empty.
The sort count is 1 if any query needs the cache, and 0 otherwise.
Repeated queries reuse the same sorted vector. Each call to cached_stats
creates its own Stats value and cache.
For example, data [8, 2, 6, 4] and queries
["count", "median", "p90", "min", "median"] return
([4, 6, 8, 2, 6], 1). The sorted copy is [2, 4, 6, 8].
This exercise uses an upper median, not the average of the two middle
values. Its p90 formula is also an explicit indexing convention;
do not substitute another percentile definition.
Why initialize through a shared reference?
Sorting a cached copy does not change the input or the statistics it describes. Methods can therefore offer a shared-reference API while updating internal cache state. This is a useful application of interior mutability.
Keep both the sort and the counter increment inside the cache initializer.
A count query can read data.len() directly, without allocating or sorting.
Why the RefCell starter fails
The last line of sorted() borrows the RefCell, obtains a reference
into its contents, and tries to return that reference. The temporary
Ref guard is dropped when the method returns. Rust rejects the escaping
reference with E0515 because it cannot outlive that guard.
RefCell can support caching with a different API: callers could retain a
mapped guard, run a closure while borrowed, or receive a copied scalar
result. It does not support the starter's attempt to return a plain slice
reference after dropping the guard. OnceCell fits the intended signature.
What OnceCell provides
get_or_init returns a reference tied to the borrow of the cell. Through
shared access, callers cannot replace an initialized value, so this needs
no runtime borrow counter. It still checks whether initialization is needed.
get() reads without initializing. set(value) returns Err(value)
if already initialized. An initializer that unwinds leaves the cell empty;
trying to initialize the same cell recursively is an error. Reading other
fields from the initializer is fine.
Write-once applies to shared access, not to every possible use of the cell.
With exclusive access, get_mut() allows mutation and take() removes
the value. A cache can therefore be invalidated by a method taking
&mut self. Existing borrows prevent that exclusive access while they
are still needed.
Related cell types
Cell<usize> suits the counter because it reads and replaces a small
Copy value without a borrow guard. Increment it with update or
set(get() + 1).
LazyCell stores its initializer at construction. OnceCell lets a
method supply one later, which makes borrowing self.data straightforward
here. Neither is Sync; they can be used in thread-local storage but
cannot directly serve as shared immutable statics. Use the synchronized
counterparts, OnceLock and LazyLock, when sharing between threads,
subject to their type bounds.
A passing submission must compile, pass the tests, and pass
clippy -D warnings.
See the OnceCell documentation for the shared-access and exclusive-access methods.
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.