#183 Jun 5, 2026

183. std::mem::take — Move Out of &mut self Without the Clone

You need the Vec out of &mut self, the borrow checker says no, so you .clone() it. Don’t. mem::take swaps in the default and hands you the original — zero allocation.

The borrow checker won’t let you move a field out of &mut self, because that would leave self half-initialized. The usual workarounds are ugly: clone the whole thing, or refactor the API to take self by value.

std::mem::take does the right thing in one line. It replaces the field with T::default() and returns the old value. For collections, Default is empty — so there’s no allocation, just a pointer swap.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use std::mem;

struct Buffer {
    items: Vec<String>,
}

impl Buffer {
    fn drain_all(&mut self) -> Vec<String> {
        // self.items                 // ❌ cannot move out of borrowed content
        // self.items.clone()         // ❌ allocates + copies every String
        mem::take(&mut self.items)    // ✅ swaps in empty Vec, returns the real one
    }
}

Where it really earns its keep is state-machine transitions, where you need to consume the data inside the current variant before swapping the variant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use std::mem;

enum Stream {
    Buffering(Vec<u8>),
    Done,
}

impl Stream {
    fn finish(&mut self) -> Vec<u8> {
        if let Stream::Buffering(buf) = self {
            let bytes = mem::take(buf);   // pull the Vec out, leave [] behind
            *self = Stream::Done;          // now safe to overwrite self
            return bytes;
        }
        Vec::new()
    }
}

Without mem::take, that pattern usually devolves into a mem::replace(self, Stream::Done) and a match on the returned value. mem::take is shorter and reads top-to-bottom.

It works for any T: DefaultString, HashMap, Option, Box<[T]>, your own structs that derive Default. If Default isn’t free for your type, reach for mem::replace and pass the sentinel you actually want.

← Previous 182. Path::with_extension — Swap a File Extension Without Slicing Strings