We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Smart Pointers and Interior Mutability step 13 of 26
Keep a guard alive with a named binding
pub fn compare_bindings() -> Vec<String>
pub fn one_form(which: usize) -> Vec<String>
Repair four blocks to demonstrate when a newly created guard is dropped.
Use the supplied Span and logging helpers rather than hard-coding the log.
Span::new(name, log) records enter <name>; its destructor records
exit <name>. mark(log, text) appends an ordinary entry.
Each block starts with its before marker and constructs its named span.
Use these forms and produce the following entries in order:
| Block | Form | Entries |
|---|---|---|
| A | let _ = Span::new("a", log); |
A: before, enter a, exit a, A: after |
| B | let _s = Span::new("b", log); |
B: before, enter b, B: after, exit b |
| C | Span::new("c", log); |
C: before, enter c, exit c, C: after |
| D | Bind Span::new("d", log) to a name, then explicitly drop it. |
D: before, enter d, D: middle, exit d, D: after |
compare_bindings() runs A, B, C and D against one fresh log and returns
their concatenated entries. one_form(which) uses a separate fresh log:
0, 1, 2 and 3 select A, B, C and D; any other index returns [].
A wildcard does not bind a value
In block A, _ is a wildcard pattern. The newly constructed Span is not
stored in a local binding, so it is dropped at the end of that statement,
before the after marker. The bare expression statement in C has the same
drop timing for this Span.
In B, _s is an ordinary variable name. Its leading underscore suppresses
the unused-variable warning; it does not shorten the value's lifetime. The
span remains owned by _s until the block ends, after the final marker.
D uses the same kind of binding but passes it to drop between its middle
and after markers.
The starter's drop(_) cannot compile: _ is not an expression naming a
value. Give the guard a name so drop(name) can consume it.
Newly created values and existing locals differ
Do not generalize A's behavior to let _ = existing_guard;. A wildcard does
not move an existing local into a new binding or explicitly destroy it. For
example, this existing span remains alive until the block ends:
let guard = Span::new("kept", log);
let _ = guard;
mark(log, "still inside");
// `guard` is dropped when its owning scope ends.
That produces enter kept, still inside, then exit kept. To end an
existing guard's lifetime here, use drop(guard) instead. See the
wildcard pattern rules.
Applying this to locks
When a freshly returned lock guard is discarded, the lock does not remain held for later statements. Bind a successfully acquired guard for the scope that needs it:
let mutex = std::sync::Mutex::new(0);
{
let mut guard = mutex.lock().expect("lock poisoned");
*guard += 1;
} // The guard is dropped and the lock released here.
rustc's deny-by-default let_underscore_lock catches discarding recognized
lock guards through a wildcard binding. The more general let_underscore_drop
lint is allow-by-default. Neither means every custom guard misuse is caught.
The Span in this exercise deliberately permits A and C so you can observe
their behavior.
Scope-based cleanup also runs on ordinary early returns and panic unwinding, but not on every form of termination, such as process abort. For this exercise, focus on the normal execution order shown in the table. Tests can compare the logs; identical output alone does not prove which binding syntax was used.
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.