We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Smart Pointers and Interior Mutability step 11 of 26
RefCell<T>: borrowing checked at runtime
Repair an operation processor and implement a borrow-state probe. Both use
RefCell<Vec<i64>>, which allows checked access to the vector through a
shared reference to the cell.
pub fn apply_ops(initial: Vec<i64>, ops: Vec<String>) -> Result<Vec<i64>, String>
pub fn safe_probe(v: Vec<i64>) -> Vec<bool>
Process the operations
Put initial in a RefCell, apply operations in order and return the final
vector in Ok. Stop at the first error and return its message in Err.
Inputs keep multiplication and intermediate sums within the i64 range.
Split each operation with split_whitespace(). Extra surrounding or
repeated whitespace is allowed, but command names are case-sensitive and
the number of tokens must match. Numeric arguments must parse as i64.
A malformed command or number returns bad op: <op>, preserving the original
operation string after the prefix.
| Operation | Behavior |
|---|---|
push <n> |
Append n. |
pop |
Remove the last value; return pop on empty if none exists. |
scale <n> |
Multiply each value by n. |
append_sum |
Append the current sum, or 0 for an empty vector. |
mirror |
Append a reversed copy of the current values. Empty input stays empty. |
require <n> |
Continue if the sum equals n; otherwise return require: want <n>, got <s>. |
In a failed requirement, <n> and <s> are formatted numeric values.
Starting from [1, 2, 3], mirror gives [1, 2, 3, 3, 2, 1].
Starting separately from [1, 2, 3], append_sum gives [1, 2, 3, 6].
The harness reports an empty error string on success. On failure, it reports
the error and an empty result vector, rather than partial operation state.
It runs safe_probe independently on the separate probe input.
Probe six borrow states
Put v in a new cell and return six booleans from this sequence. Do not
modify its values or trigger a borrow panic.
- Take a shared guard
a. Record whether the vector is empty. - While holding
a, record whethertry_borrow()succeeds. - While holding
a, record whethertry_borrow_mut()succeeds. - Drop
a, then take a mutable guardb. Record whether every value is nonnegative. This istruefor an empty vector. - While holding
b, record whethertry_borrow()succeeds. - Drop
b, then record whethertry_borrow_mut()succeeds.
Each successful trial borrow should be released before the next step.
Calling .is_ok() without retaining the result's guard does this. For
example, an empty input produces [true, true, false, true, false, true].
Borrow guards enforce access at runtime
borrow() returns a Ref guard for shared access. borrow_mut() returns a
RefMut guard for mutable access. A new shared borrow is incompatible with
an active mutable borrow; a new mutable borrow requires no active borrows.
Dropping a guard releases the access it represents.
The panicking methods reject conflicts with a panic. try_borrow() and
try_borrow_mut() instead return an error, which is useful when a conflict
is an expected outcome, as in safe_probe. These checks supplement Rust's
compile-time rules; they do not turn those rules off. Exact panic wording
and the cell's internal representation are not part of this exercise.
Cell<Vec<i64>> is also usable, for example by taking the vector out,
modifying it and putting it back. RefCell is useful here because it lets
callers access the contained vector through borrow guards.
Return the guard from peek
The starter tries to return a reference obtained through a temporary guard:
fn peek(cell: &RefCell<Vec<i64>>) -> &Vec<i64> {
&cell.borrow()
}
This produces E0515. The returned reference would outlast the guard that
keeps its shared borrow active. Return Ref<'_, Vec<i64>> instead, with
cell.borrow() as the body, and import Ref from std::cell.
The caller can read the vector through the returned guard and controls when that guard is dropped. Returning owned data or doing work inside a callback are other API designs, but this exercise uses a guard-returning helper.
End the read before starting the write
Fixing peek exposes a conflict in append_sum:
data.borrow_mut().push(peek(&data).iter().sum());
The method receiver is evaluated before its argument. The mutable guard
therefore exists when peek requests a shared borrow of the same cell.
Compute the sum in a separate statement, then acquire the mutable guard to
append it. The temporary read guard ends before the write begins.
The starter's mirror arm has a similar conflict:
for x in peek(&data).iter() {
data.borrow_mut().push(*x);
}
The loop keeps the shared guard alive while iterating. For nonempty input, the first attempted mutable borrow panics. For empty input, the loop body does not run. Even without that borrow conflict, forward iteration would not produce the reversed copy the operation requires.
Collect the reversed values into an owned vector in one statement, then extend the cell's vector in a second statement. If you bind a read guard separately, copying values does not itself drop that guard: explicitly drop it or end its scope before writing.
Thread-safety is a separate property
RefCell<T> can be moved to another thread when T: Send, but it is not
Sync. Constructing Arc<RefCell<T>> can compile; attempting to share that
combination between threads fails the required thread-safety bounds.
Arc provides shared ownership, not synchronization of its contents.
A mutex is one option when threads need coordinated mutable access.
Check operation results, exact error text and the six probe values. Passing requires compilation, correct behavior and 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.