Rust mem::take and mem::replace
mem::take and mem::replace are helpers that move a value out of a borrowed place. Each helper swaps the old value out and leaves a stand-in behind.
The wall
This move is refused (error E0507): a value behind &mut may not be taken out if nothing is left behind.
fn rename(bag: &mut Bag) {
let name = bag.name; // refused: cannot move out of borrowed content
}mem::take swaps in the default
take hands you the old value and leaves the type's Default in its place — for a String, an empty one.
let sign = mem::take(sign);
*door = Door::Closed;Take, then rebuild
The classic job is to change an enum variant in place. It reuses a String the enum already owns. No clone, no new allocation. A close helper does it for a door. It takes the sign text out of the Open variant. It hands the door back Closed.
let mut door = Door::Open { sign: String::from("open") };
println!("took: {}", close(&mut door));
println!("door: {door:?}");mem::replace picks the stand-in
replace is take with a stand-in of your choosing. It works even when the type has no Default.
let mut count = 5;
let old = mem::replace(&mut count, 0);
println!("old = {old}, count = {count}");Option has the trick built in
For an Option, skip mem entirely — its own take() leaves None behind.
let mut slot = Some(String::from("key"));
let taken = slot.take();mem::swap exchanges two values
swap is the third helper in the family: no stand-in, no returned value — the two places simply trade contents.
let mut first = String::from("blue");
let mut second = String::from("red");
mem::swap(&mut first, &mut second);
println!("first = {first}, second = {second}");Swap two fields of one struct
Two fields of the same struct can trade places in one call. The checker can see they are different places. So both mutable borrows are allowed at once. (Pair here is a struct with left and right i32 fields.)
let mut pair = Pair { left: 1, right: 2 };
mem::swap(&mut pair.left, &mut pair.right);
println!("left = {}, right = {}", pair.left, pair.right);Example
use std::mem;
#[derive(Debug)]
enum Job {
Queued { title: String },
Running { title: String },
Done,
}
fn start(job: &mut Job) {
if let Job::Queued { title } = job {
let title = mem::take(title);
*job = Job::Running { title };
}
}
fn finish(job: &mut Job) {
let old = mem::replace(job, Job::Done);
if let Job::Running { title } = old {
println!("finished: {title}");
}
}
fn main() {
let mut job = Job::Queued { title: String::from("backup") };
start(&mut job);
println!("now: {job:?}");
finish(&mut job);
println!("now: {job:?}");
}Key points
- Use mem::take(&mut place) to move a value out of a borrowed place.
- The place keeps the type's Default value.
- The caller receives the old value.
- mem::take needs a Default: String gives an empty string, Vec an empty vec, numbers give 0 and bool gives false
- Use mem::replace(&mut place, new) when the type has no Default.
- Use it, too, when the stand-in is chosen by hand.
- It returns the old value.
- For Option<T>, skip mem: the method .take() does the same swap and leaves None behind
- Use mem::swap(&mut a, &mut b) to exchange two values in place.
- Each one ends up holding the other's old value.
- Neither place needs a Default stand-in.
In this example, a Job starts as Queued with the title "backup". start matches the Queued variant through the &mut. It takes the title out with mem::take. An empty string sits in the old value for a moment. Then start stores a Running job built from the real title. finish then uses mem::replace to swap the whole job for Done. It keeps the old Running value and prints its title. The job is rebuilt twice, and the title String is never cloned.
Note
The stand-in is a real value, never a hole.
After a take, the place is safe to read at every moment.
It simply holds the default until something better is stored.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Inside a method with &mut self, you must move a String field out and keep the struct valid. What do you reach for?