Theme
Q034. How can you safely extract an owned value using Option::take, mem::take, or mem::replace?
Short Answer
These APIs move a value out through mutable access while immediately leaving valid replacement state. Option::take replaces Some(value) with None; mem::take replaces a value with Default::default(); and mem::replace installs an explicit replacement. Each returns ownership of the previous value without cloning and without leaving the borrowed location uninitialized.
Deep Dive
An &mut T grants exclusive mutation but does not own the T, so moving directly through it would leave the original owner with invalid storage. Replacement APIs combine the move-out and write-back into one valid operation.
Choose based on the replacement state:
Option::take(&mut self)naturally represents an optional slot and leavesNone;mem::take(&mut value)requiresT: Defaultand leaves the default value;mem::replace(&mut value, replacement)works with an explicit replacement and has noDefaultbound.
The returned old value remains alive under the caller's ownership. It is not dropped by the replacement operation.
Internal Model
Conceptually, these operations read the old value without dropping it, write a valid replacement, and return the old value. Their implementations use low-level moves internally while presenting a safe atomic ownership transition.
Drop analysis sees the borrowed place as initialized throughout the operation, preventing double drop or an observable uninitialized hole.
Example
rust
use std::mem;
struct Queue {
next: Option<String>,
}
fn main() {
let mut queue = Queue {
next: Some(String::from("job")),
};
let job = queue.next.take().unwrap();
let mut status = String::from("queued");
let previous = mem::replace(&mut status, String::from("running"));
let current = mem::take(&mut status);
println!("{job} {previous} {current} {status:?}");
}Common Mistakes
- Believing
&mut Tpermits leaving the referent uninitialized. - Assuming
mem::replacedrops the old value instead of returning it. - Using
clonewhen ownership can be transferred with a valid replacement.
Follow-ups
- How can
Option<T>encode a temporarily empty field safely? - What performance difference can
Vec::take-style replacement have from cloning? - When is
ptr::readrequired instead of these safe APIs?
Related
- Q018. Why can't you move a value out of borrowed or indexed content?
- Q027. What is the difference between
Drop::dropandstd::mem::drop? - Q032. How does ownership work for struct fields?
References
- The Rust Programming Language
- Rust Reference
- Rust Standard Library