Skip to content

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 leaves None;
  • mem::take(&mut value) requires T: Default and leaves the default value;
  • mem::replace(&mut value, replacement) works with an explicit replacement and has no Default bound.

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 T permits leaving the referent uninitialized.
  • Assuming mem::replace drops the old value instead of returning it.
  • Using clone when 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::read required instead of these safe APIs?

References

  • The Rust Programming Language
  • Rust Reference
  • Rust Standard Library