Vec

#150 May 2026

150. Vec::spare_capacity_mut — Fill a Vec From a Callback Without Zeroing It First

You reserve 4 KiB to read from a socket, and to hand the buffer over you first… write 4096 zeros. Vec::spare_capacity_mut exposes the reserved-but-uninitialized tail as &mut [MaybeUninit<u8>] so the callback writes straight into the allocation.

The pain: paying to overwrite

The intuitive fill-a-buffer pattern resizes the Vec first so the slice exists:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let mut buf: Vec<u8> = Vec::with_capacity(8);
buf.resize(8, 0); // writes 8 zeros we're about to clobber

// Pretend this is `read(fd, buf.as_mut_ptr(), buf.len())`.
fill(&mut buf);

assert_eq!(buf, b"rustbite");

fn fill(out: &mut [u8]) {
    out.copy_from_slice(b"rustbite");
}

It works, but resize walks the whole tail writing zeros that the next line overwrites — a measurable cost for big reads, and pointless for types where “zero” isn’t even a valid value.

The fix: write into the uninitialized tail

Vec::spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] hands you a slice covering exactly capacity - len slots. Write into them, then call set_len to tell the Vec they’re now initialized:

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

let mut buf: Vec<u8> = Vec::with_capacity(8);

// View the reserved-but-uninitialized tail.
let spare: &mut [MaybeUninit<u8>] = buf.spare_capacity_mut();
assert_eq!(spare.len(), 8);

// Write through the MaybeUninit pointer — no zeroing first.
for (slot, byte) in spare.iter_mut().zip(b"rustbite") {
    slot.write(*byte);
}

// Promise the Vec those 8 slots are now valid `u8`s.
unsafe { buf.set_len(8); }

assert_eq!(buf, b"rustbite");

spare_capacity_mut itself is safe — MaybeUninit<T> is the type that lets you hold “maybe garbage” without UB. The unsafe block is just the set_len call where you assert you really did initialize them.

Pairing with a real fill API

The standard pattern is “reserve, write, set_len”:

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

fn read_bytes(buf: &mut Vec<u8>, extra: usize, source: &[u8]) {
    buf.reserve(extra);

    let spare = buf.spare_capacity_mut();
    let n = extra.min(source.len()).min(spare.len());

    // Initialize the prefix we actually wrote.
    for i in 0..n {
        spare[i].write(source[i]);
    }

    // Only extend by what's been initialized.
    unsafe { buf.set_len(buf.len() + n); }
}

let mut v = vec![b'>', b' '];
read_bytes(&mut v, 8, b"rustbite");
assert_eq!(v, b"> rustbite");

The same shape works with read-style callbacks: cast the MaybeUninit<u8> slice to a raw pointer, hand it to C, and only extend len by the byte count the call returned. The bytes you didn’t write stay MaybeUninit — never read them.

When to reach for it

Reading from sockets, files, or FFI fill-style APIs into a Vec<u8> is the headline use case — every tokio and mio read path eventually bottoms out in this pattern. It’s also useful for non-Copy types where there’s no sensible default to seed with: image decoders writing Vec<Pixel>, audio decoders writing Vec<f32>, parser arenas writing Vec<Node>.

If you don’t need the spare capacity view — you’re building up element-by-element — Vec::push (or Vec::push_mut from bite 88) is still the right call. spare_capacity_mut is the tool for the moment you have an external writer that wants a flat buffer and you’d rather not pay to zero it first.

#143 May 2026

143. Vec::dedup_by_key — Collapse Consecutive Duplicates by a Derived Key

Vec::dedup() only collapses runs that are exactly equal. When you care about a derived attribute — the minute on a timestamp, the domain in an email, the first letter of a word — reach for dedup_by_key.

The plain dedup() is strict: two adjacent elements are only merged if == says so.

1
2
3
let mut nums = vec![1, 1, 2, 3, 3, 3, 2, 5];
nums.dedup();
assert_eq!(nums, vec![1, 2, 3, 2, 5]);

But often “duplicate” really means “shares some property with its neighbour.” dedup_by_key takes a closure that maps each element to a key, then keeps the first of every consecutive run whose keys match:

1
2
3
let mut nums = vec![1, 3, 5, 2, 4, 7, 6, 8];
nums.dedup_by_key(|n| *n % 2);
assert_eq!(nums, vec![1, 2, 7, 6]);

1, 3, 5 all have key 1 → keep 1. Then 2, 4 have key 0 → keep 2. Then 7 has key 1 → keep it. Then 6, 8 have key 0 → keep 6.

The practical case: log lines already in time order, and you want one representative per minute.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
let mut lines = vec![
    "12:01 GET /a".to_string(),
    "12:01 GET /b".to_string(),
    "12:01 POST /c".to_string(),
    "12:02 GET /d".to_string(),
    "12:02 GET /e".to_string(),
    "12:03 PATCH /f".to_string(),
];

lines.dedup_by_key(|line| line[..5].to_string());

assert_eq!(lines.len(), 3);
assert_eq!(lines[0], "12:01 GET /a");
assert_eq!(lines[1], "12:02 GET /d");
assert_eq!(lines[2], "12:03 PATCH /f");

Two things worth remembering. First, it only looks at adjacent pairs — if you need full uniqueness, pair it with sort_by_key first. Second, if your equivalence isn’t expressible as a key (e.g. “values within 0.1 of each other”), there’s a sibling dedup_by that takes |a, b| -> bool directly. All three are in-place, allocation-free, and run in linear time.

#144 May 2026

144. Vec::into_raw_parts — Hand a Vec to C Without the ManuallyDrop Dance

You want to give a Rust-allocated buffer to C and re-take it later. That means handing over (ptr, len, capacity) — and historically, prying those three out of a Vec without freeing the allocation meant wrapping the vector in ManuallyDrop first. Rust 1.93 stabilises Vec::into_raw_parts, a single safe call that returns the triple and consumes the Vec for you.

The pain: extracting parts while suppressing drop

The classic recipe leaks the Vec’s destructor on purpose so the C side owns the memory. You need three reads and a guard to keep Drop from racing the allocator:

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

let v: Vec<u32> = vec![10, 20, 30];

let mut me = ManuallyDrop::new(v);
let ptr = me.as_mut_ptr();
let len = me.len();
let cap = me.capacity();

assert_eq!(unsafe { *ptr.add(1) }, 20);
assert_eq!((len, cap), (3, 3));

// Hand (ptr, len, cap) to C here.
// Reclaim it later with Vec::from_raw_parts to free the allocation.
let _reclaimed = unsafe { Vec::from_raw_parts(ptr, len, cap) };

It works, but the ManuallyDrop wrapper exists only to keep the destructor from running. Forget it, write mem::forget(v) in the wrong order, or read capacity() after the move and you’ve got a use-after-free or a leak.

The fix: one safe call, three return values

Vec::into_raw_parts(self) -> (*mut T, usize, usize) consumes the Vec, hands you the pointer-length-capacity triple, and leaves the allocation alive for you to manage:

1
2
3
4
5
6
7
8
9
let v: Vec<u32> = vec![10, 20, 30];
let (ptr, len, cap) = v.into_raw_parts();

assert_eq!((len, cap), (3, 3));
assert_eq!(unsafe { *ptr.add(1) }, 20);

// Reclaim and free at the end (or hand to C and have C call back).
let reclaimed = unsafe { Vec::from_raw_parts(ptr, len, cap) };
assert_eq!(reclaimed, vec![10, 20, 30]);

No wrapper, no separate field reads, no chance of accidentally calling a &self method after the move. The method is const, too.

String::into_raw_parts follows the same shape

String gets the same treatment in 1.93. The triple is (*mut u8, usize, usize), which is what String::from_raw_parts wants back:

1
2
3
4
5
6
7
let s = String::from("hello");
let (ptr, len, cap) = s.into_raw_parts();

assert_eq!((len, cap), (5, 5));

let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) };
assert_eq!(rebuilt, "hello");

The pairing is the point: into_raw_parts is safe (the Vec/String is gone, no aliasing exists yet), and from_raw_parts is unsafe (you’re asserting the triple came from a matching allocator with the right layout). The split keeps the unsafety where it actually lives.

When to reach for it

Any FFI boundary where the C side will hold the buffer for a while: graphics buffers, codec frames, command queues, anything with an extern "C" fn free_my_thing(ptr, len, cap) callback. Also handy when you’re building your own typed handles around a raw allocation — Box::into_raw covers the single-value case; into_raw_parts covers the variable-length one.

If you only need the pointer and nothing will ever reclaim the allocation, Vec::leak is still the shorter call. Reach for into_raw_parts the moment the capacity matters — i.e. anyone, anywhere, might want to give the memory back.

#137 May 2026

137. Vec::retain_mut — Filter and Edit In Place, In One Pass

retain decides who stays, but its closure only sees &T — so when you also want to tweak the survivors, you end up making two passes. retain_mut collapses that into one.

The Problem

Imagine a Vec<Job> where each surviving job needs its retry counter bumped on the way through. With plain retain, the closure gets a shared reference, so the mutation has to happen separately:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
struct Job { id: u32, retries: u32, done: bool }

let mut jobs = vec![
    Job { id: 1, retries: 0, done: false },
    Job { id: 2, retries: 0, done: true  },
    Job { id: 3, retries: 0, done: false },
];

// Pass 1: bump the survivors.
for j in jobs.iter_mut().filter(|j| !j.done) {
    j.retries += 1;
}
// Pass 2: drop the finished ones.
jobs.retain(|j| !j.done);

Two passes, two intent-leaks, and the predicate is duplicated. Refactor one and forget to refactor the other — welcome to a subtle bug.

retain_mut: One Pass, Mutable Access

Vec::retain_mut gives the closure a &mut T. Edit the element and return whether to keep it — same call:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
struct Job { id: u32, retries: u32, done: bool }

let mut jobs = vec![
    Job { id: 1, retries: 0, done: false },
    Job { id: 2, retries: 0, done: true  },
    Job { id: 3, retries: 0, done: false },
];

jobs.retain_mut(|j| {
    if j.done { return false; }
    j.retries += 1;
    true
});

assert_eq!(jobs.len(), 2);
assert_eq!(jobs[0].id, 1);
assert_eq!(jobs[0].retries, 1);
assert_eq!(jobs[1].id, 3);
assert_eq!(jobs[1].retries, 1);

One scan, no duplicated predicate, and the order of the kept elements is preserved.

The Sneaky Trick: Mutate-Then-Check

The mutation happens before the closure returns, so a retain_mut can also normalize values and then filter on the normalized form:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let mut words = vec![
    String::from("  hello "),
    String::from("   "),
    String::from(" Rust"),
];

words.retain_mut(|s| {
    *s = s.trim().to_string();
    !s.is_empty()
});

assert_eq!(words, vec!["hello", "Rust"]);

Trim everything, then drop the blanks — without ever allocating a second Vec.

When to Reach for It

Use retain_mut whenever the keep/drop decision and an in-place edit travel together. Same goes for VecDeque::retain_mut and LinkedList::retain_mut — same shape, same payoff. If the closure is purely read-only, stick with retain and keep the intent narrow.

#126 May 2026

126. Vec::split_off — Cut a Vec in Two and Keep Both Halves

You want the tail of a Vec as its own owned collection — the head stays put, the tail walks away. Cloning a slice works for Clone types, but breaks the moment your elements aren’t cloneable. Vec::split_off doesn’t care.

The clone-and-truncate dance

The textbook split: copy the tail with to_vec(), then truncate the original.

1
2
3
4
5
6
7
let mut all = vec![1, 2, 3, 4, 5, 6];

let tail: Vec<i32> = all[3..].to_vec();
all.truncate(3);

assert_eq!(all,  vec![1, 2, 3]);
assert_eq!(tail, vec![4, 5, 6]);

It works, but it clones every element of the tail. Fine for i32, wasteful for String, and a hard error for any type that isn’t Clone.

split_off moves, doesn’t clone

Vec::split_off(at) consumes the elements at at.. out of the original Vec and returns them as a new Vec. The elements are moved, not copied — so it works for any T, Clone or not:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let mut tasks: Vec<Box<dyn FnOnce()>> = vec![
    Box::new(|| println!("a")),
    Box::new(|| println!("b")),
    Box::new(|| println!("c")),
    Box::new(|| println!("d")),
];

// Box<dyn FnOnce()> isn't Clone — `tasks[2..].to_vec()` won't compile.
let later = tasks.split_off(2);

assert_eq!(tasks.len(), 2);
assert_eq!(later.len(), 2);

The original keeps [0..at), the returned Vec gets [at..len), and not a single element is duplicated. at == 0 gives you the whole thing in the new Vec (the original ends up empty); at == len gives you an empty new Vec. Anything past the length panics.

A real shape: page-by-page draining

split_off shines when you want to peel a batch off the front of a queue and hand it to a worker, keeping the rest for next time:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
fn next_batch<T>(queue: &mut Vec<T>, size: usize) -> Vec<T> {
    let take = size.min(queue.len());
    let rest = queue.split_off(take);
    // queue now holds the first `take` items — that's our batch.
    // `rest` holds everything else — put it back as the new queue.
    let batch = std::mem::replace(queue, rest);
    batch
}

let mut q = vec!["a", "b", "c", "d", "e"];
let first  = next_batch(&mut q, 2);
let second = next_batch(&mut q, 2);

assert_eq!(first,  vec!["a", "b"]);
assert_eq!(second, vec!["c", "d"]);
assert_eq!(q,      vec!["e"]);

No clone, no temporary Vec, no fighting the borrow checker over slice ranges. Two memcpys and you’re done.

When to reach for it

Use split_off whenever you need both halves of a Vec as owned collections — batching, chunked processing, splitting state between threads. If you only want to iterate the tail and throw it away, drain(at..) is better; if you want to keep it, split_off is the move.

#115 May 2026

115. Vec::resize_with — Grow a Vec With a Closure, Not a Clone

Vec::resize makes every new slot a clone of the same value. When you need fresh values per slot — counters, allocations, defaults — resize_with calls a closure for each new element instead.

Vec::resize(n, value) is fine when the filler is cheap and identical, but it has two annoyances. It needs T: Clone, and every new slot is the same clone. So this doesn’t work the way you want:

1
2
3
4
5
// One Vec shared across every slot — mutating slot 0 mutates them all.
let mut grid: Vec<Vec<u8>> = Vec::new();
grid.resize(3, Vec::new());
grid[0].push(42);
assert_eq!(grid[1], vec![]); // fine — Vec::new() clones to a new empty Vec

That one happens to be safe because Vec::clone actually allocates. But the moment your T is Rc<RefCell<…>>, every slot points at the same cell. And if T isn’t Clone at all, you can’t call resize in the first place.

resize_with takes a closure and calls it once per new slot:

1
2
3
4
5
6
7
let mut counter = 0;
let mut v = vec![10, 20];
v.resize_with(5, || {
    counter += 1;
    counter
});
assert_eq!(v, vec![10, 20, 1, 2, 3]);

The closure can capture mutable state, so each call is fresh. Generating IDs, pulling from an RNG, allocating independent buffers — all easy:

1
2
3
4
5
6
7
8
9
let mut next_id = 100;
let mut buffers: Vec<(usize, Vec<u8>)> = Vec::new();
buffers.resize_with(3, || {
    let id = next_id;
    next_id += 1;
    (id, Vec::with_capacity(1024))
});
assert_eq!(buffers[0].0, 100);
assert_eq!(buffers[2].0, 102);

For non-Clone types, Default::default is the usual filler:

1
2
3
4
5
6
7
8
9
#[derive(Default, Debug, PartialEq)]
struct Slot {
    open: bool,
    payload: Vec<u8>,
}

let mut slots: Vec<Slot> = Vec::new();
slots.resize_with(2, Default::default);
assert_eq!(slots, vec![Slot::default(), Slot::default()]);

Shrinking still works, and the closure is never called when the new length is smaller:

1
2
3
let mut v = vec![1, 2, 3, 4, 5];
v.resize_with(2, || unreachable!());
assert_eq!(v, vec![1, 2]);

Reach for resize_with whenever the filler isn’t a single static value — and especially when T doesn’t (or shouldn’t) implement Clone.

#111 Apr 2026

111. Vec::insert_mut — Splice In and Edit Without Reindexing

You insert a placeholder, then index back to fix it up. Two lookups, two bounds checks, one wobble. Vec::insert_mut — stable in 1.95 — hands you the &mut T directly.

The classic dance:

1
2
3
4
let mut v = vec![1, 2, 4, 5];
v.insert(2, 0);
v[2] = 3; // recompute the index, second bounds check
assert_eq!(v, [1, 2, 3, 4, 5]);

insert_mut returns the slot:

1
2
3
4
let mut v = vec![1, 2, 4, 5];
let slot = v.insert_mut(2, 0);
*slot = 3;
assert_eq!(v, [1, 2, 3, 4, 5]);

This shines when the value is built in pieces — push a default, then fill it in based on where it landed:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#[derive(Default, Debug, PartialEq)]
struct Job { id: u32, label: String }

let mut jobs: Vec<Job> = vec![Job { id: 1, label: "build".into() }];
let j = jobs.insert_mut(0, Job::default());
j.id = 0;
j.label = "setup".into();

assert_eq!(jobs[0], Job { id: 0, label: String::from("setup") });
assert_eq!(jobs[1].label, "build");

Rust 1.95 grew the whole _mut family alongside Vec::push_mut: Vec::insert_mut, VecDeque::push_front_mut, VecDeque::push_back_mut, VecDeque::insert_mut, LinkedList::push_front_mut, and LinkedList::push_back_mut. Same idea everywhere — the place-it method now returns a mutable reference to the slot it just placed.

1
2
3
4
5
6
use std::collections::VecDeque;

let mut q: VecDeque<i32> = VecDeque::from([2, 3]);
let head = q.push_front_mut(0);
*head += 1; // now 1
assert_eq!(q, VecDeque::from([1, 2, 3]));

Quietly useful, no churn — just fewer indices floating around.

#107 Apr 2026

107. Vec::splice — Replace a Range and Keep the Old Values

Need to swap a chunk of a Vec for a different number of items? Most people reach for drain plus extend and a bunch of bookkeeping. Vec::splice does the whole thing in one call — and even hands back what it removed.

The clunky way

You want to replace v[1..4] with two different values. Without splice, you end up juggling indices:

1
2
3
4
5
6
7
8
9
let mut v = vec![1, 2, 3, 4, 5];

// Remove the middle, save the tail, then stitch it back together.
let tail: Vec<_> = v.drain(4..).collect();
v.truncate(1);
v.extend([10, 20]);
v.extend(tail);

assert_eq!(v, [1, 10, 20, 5]);

Three steps, one temporary Vec, and you didn’t even capture what was removed. Easy to off‑by‑one, easy to get wrong.

Enter splice

splice takes a range and an iterator and swaps them in place — the replacement can be any length:

1
2
3
4
5
let mut v = vec![1, 2, 3, 4, 5];

v.splice(1..4, [10, 20]);

assert_eq!(v, [1, 10, 20, 5]);

One call. No tail buffer. Works whether the replacement is shorter, longer, or the same size as the range.

It returns the removed items too

splice is a draining iterator — collect it and you get the elements that were taken out:

1
2
3
4
5
6
let mut v = vec![10, 20, 30, 40, 50];

let removed: Vec<_> = v.splice(1..4, [99]).collect();

assert_eq!(removed, [20, 30, 40]);
assert_eq!(v, [10, 99, 50]);

Perfect for “swap this section, but undo it later” patterns.

Insert without removing

Pass an empty range and splice becomes a multi‑item insert:

1
2
3
4
5
let mut v = vec![1, 5];

v.splice(1..1, [2, 3, 4]);

assert_eq!(v, [1, 2, 3, 4, 5]);

That’s far nicer than calling insert in a loop and watching every push shift the tail again.

When to reach for it

Any time you’d write drain(..) followed by extend(..) or a tower of insert calls, try splice first. One method, one allocation pattern, and the removed items come along for free.

#105 Apr 2026

105. Vec::extend_from_within — Duplicate a Range Without Fighting the Borrow Checker

Trying to copy a slice of a Vec back onto the end of itself? v.extend_from_slice(&v[..k]) won’t compile — &v and &mut v collide. extend_from_within is the one-call answer.

The borrow checker says no

It looks innocent: take the first few bytes of a buffer and append them to the end. The naïve write doesn’t even reach the type checker without complaint:

1
2
3
let mut buf: Vec<u8> = vec![0xCA, 0xFE, 0xBA, 0xBE];
// buf.extend_from_slice(&buf[..2]);
// ^^ error: cannot borrow `buf` as immutable because it is also borrowed as mutable

extend_from_slice needs &mut self, and &buf[..2] is an immutable borrow of the same Vec. Two borrows, one of them mutable, same value — instant rejection.

The usual workarounds bloat the call site. Either make a temporary copy, or reach for unsafe and a raw pointer dance:

1
2
3
4
5
6
let mut buf: Vec<u8> = vec![0xCA, 0xFE, 0xBA, 0xBE];

let head: Vec<u8> = buf[..2].to_vec();   // extra allocation
buf.extend_from_slice(&head);

assert_eq!(buf, [0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE]);

A whole heap allocation just to copy two bytes inside a vector you already own. There’s a better way.

extend_from_within — one call, no temp

Vec::extend_from_within takes a range into self and appends those elements. No second buffer, no unsafe, no fight with the borrow checker:

1
2
3
4
let mut buf: Vec<u8> = vec![0xCA, 0xFE, 0xBA, 0xBE];
buf.extend_from_within(..2);

assert_eq!(buf, [0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE]);

It accepts any RangeBounds<usize>..k, i..j, i..=j, .. — so you can grab whatever slice you want, including the whole Vec:

1
2
3
4
let mut v = vec![1, 2, 3];
v.extend_from_within(..);   // double the contents

assert_eq!(v, [1, 2, 3, 1, 2, 3]);

It requires T: Clone, and clones each element exactly once. For Copy types like primitives it lowers to a single memcpy.

A real use: building patterns

It’s especially nice for building repeating or growing patterns where each step depends on the previous one — think DSP buffers, simple test fixtures, or doubling tricks:

1
2
3
4
5
6
7
let mut data = vec![1u32];
for _ in 0..4 {
    data.extend_from_within(..); // double in place
}

assert_eq!(data.len(), 16);
assert!(data.iter().all(|&x| x == 1));

When to reach for it

Any time you’d write v.extend_from_slice(&v[range]) and the borrow checker stops you, swap in v.extend_from_within(range). Stable since Rust 1.53, exists for both Vec<T> and VecDeque<T>, and quietly turns a frustrating compile error into a one-liner.

#104 Apr 2026

104. Vec::swap_remove — O(1) Removal When Order Doesn't Matter

Calling Vec::remove(i) shifts every element after i left by one — that’s O(n) per call. If you don’t care about order, swap_remove does the same job in O(1).

The hidden cost of Vec::remove

remove takes the value out of the vector, but to keep the rest in order it has to shift every following element left by one slot. On a long vector, removing near the front is a giant memcpy:

1
2
3
4
5
let mut v = vec![1, 2, 3, 4, 5];
let removed = v.remove(1);

assert_eq!(removed, 2);
assert_eq!(v, [1, 3, 4, 5]);

Inside a loop — “remove every item that matches X” — that O(n) cost compounds into O(n²). A tight loop that should be linear silently becomes quadratic.

swap_remove: trade order for speed

swap_remove(i) removes the element at index i by swapping it with the last element, then popping the tail. No shifting, no memcpy chain — just one swap and a pop:

1
2
3
4
5
6
let mut v = vec![1, 2, 3, 4, 5];
let removed = v.swap_remove(1);

assert_eq!(removed, 2);
// 5 took 2's spot — order is gone, but the op was O(1).
assert_eq!(v, [1, 5, 3, 4]);

If your Vec is really a set — entities in a game world, pending jobs in a worker pool, items keyed by id elsewhere — order rarely matters. swap_remove is free.

Removing many items in place

The classic mistake: iterate forward calling remove. swap_remove lets you do it without the O(n²) blow-up. Walk by index and don’t bump the cursor when you remove:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let mut nums = vec![1, 2, 3, 4, 5, 6, 7, 8];

let mut i = 0;
while i < nums.len() {
    if nums[i] % 2 == 0 {
        nums.swap_remove(i);
        // Don't increment — a new element is now at position i.
    } else {
        i += 1;
    }
}

nums.sort(); // canonicalize for the assert
assert_eq!(nums, [1, 3, 5, 7]);

For bulk predicate-based filtering, Vec::retain stays cleaner (and is also O(n)). Reach for swap_remove when you have a specific index you want gone in O(1).

With structs

swap_remove returns the removed value, so you can hand it off to another collection on the way out:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#[derive(PartialEq, Debug)]
struct Job { id: u32 }

let mut queue = vec![
    Job { id: 1 },
    Job { id: 2 },
    Job { id: 3 },
    Job { id: 4 },
];

let pulled = queue.swap_remove(0);

assert_eq!(pulled, Job { id: 1 });
assert_eq!(queue.len(), 3);

When to reach for it

Use swap_remove whenever you’d write remove(i) and the surrounding code doesn’t depend on element order. Order-preserving removal still needs remove, but most “remove one item from this Vec” code is order-agnostic — and swap_remove turns an O(n) pothole into a one-line O(1) win. Stable since Rust 1.0, one of those quietly-essential APIs that’s easy to forget exists.

88. Vec::push_mut — Push and Modify in One Step

Tired of pushing a default into a Vec and then grabbing a mutable reference to fill it in? Rust 1.95 stabilises push_mut, which returns &mut T to the element it just inserted.

The old dance

A common pattern is to push a placeholder value and then immediately mutate it. Before push_mut, you had to do an awkward two-step:

1
2
3
4
5
6
7
8
9
let mut names = Vec::new();

// Push, then index back in to mutate
names.push(String::new());
let last = names.last_mut().unwrap();
last.push_str("hello");
last.push_str(", world");

assert_eq!(names[0], "hello, world");

That last_mut().unwrap() is boilerplate — you know the element is there because you just pushed it. Worse, in more complex code the compiler can’t always prove the reference is safe, forcing you into index gymnastics.

Enter push_mut

push_mut pushes the value and hands you back a mutable reference in one shot:

1
2
3
4
5
6
7
let mut scores = Vec::new();

let entry = scores.push_mut(0);
*entry += 10;
*entry += 25;

assert_eq!(scores[0], 35);

No unwraps, no indexing, no second lookup. The borrow checker is happy because there’s a clear chain of ownership.

Building structs in-place

This really shines when you’re constructing complex values piece by piece:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#[derive(Debug, Default)]
struct Player {
    name: String,
    score: u32,
    active: bool,
}

let mut roster = Vec::new();

let p = roster.push_mut(Player::default());
p.name = String::from("Ferris");
p.score = 100;
p.active = true;

assert_eq!(roster[0].name, "Ferris");
assert_eq!(roster[0].score, 100);
assert!(roster[0].active);

Instead of building the struct fully before pushing, you push a default and fill it in. This can be handy when the final field values depend on context you only have after insertion.

Also works for insert_mut

Need to insert at a specific index? insert_mut follows the same pattern:

1
2
3
4
5
let mut v = vec![1, 3, 4];
let inserted = v.insert_mut(1, 2);
*inserted *= 10;

assert_eq!(v, [1, 20, 3, 4]);

Both methods are also available on VecDeque (push_front_mut, push_back_mut, insert_mut) and LinkedList (push_front_mut, push_back_mut).

When to reach for it

Use push_mut whenever you’d otherwise write push followed by last_mut().unwrap(). It’s one less unwrap, one fewer line, and clearer intent: push this, then let me tweak it.

Stabilised in Rust 1.95 (April 2026) — update your toolchain and give it a spin.

#047 Mar 2026

47. Vec::pop_if — Conditionally Pop the Last Element

Need to remove the last element of a Vec only when it meets a condition? Vec::pop_if does exactly that — no index juggling, no separate check-then-pop.

The old way

Before pop_if, you’d write something like this:

1
2
3
4
5
6
let mut stack = vec![1, 2, 3, 4];

if stack.last().is_some_and(|x| *x > 3) {
    let val = stack.pop().unwrap();
    println!("Popped: {val}");
}

Two separate calls, and a subtle TOCTOU gap if you’re not careful — last() checks one thing, then pop() acts on an assumption.

Enter pop_if

Stabilized in Rust 1.86, pop_if combines the check and the removal into one atomic operation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let mut stack = vec![1, 2, 3, 4];

// Pop the last element only if it's greater than 3
let popped = stack.pop_if(|x| *x > 3);
assert_eq!(popped, Some(4));
assert_eq!(stack, [1, 2, 3]);

// Now the last element is 3 — doesn't match, so nothing happens
let stayed = stack.pop_if(|x| *x > 3);
assert_eq!(stayed, None);
assert_eq!(stack, [1, 2, 3]);

The closure receives a &mut T reference to the last element. If it returns true, the element is removed and returned as Some(T). If false (or the vec is empty), you get None.

Mutable access inside the predicate

Because the closure gets &mut T, you can even modify the element before deciding whether to pop it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let mut tasks = vec![
    String::from("buy milk"),
    String::from("URGENT: deploy fix"),
];

let urgent = tasks.pop_if(|task| {
    if task.starts_with("URGENT:") {
        *task = task.replacen("URGENT: ", "", 1);
        true
    } else {
        false
    }
});

assert_eq!(urgent.as_deref(), Some("deploy fix"));
assert_eq!(tasks, vec!["buy milk"]);

A practical use: draining from the back

pop_if is handy for processing a sorted vec from the tail. Think of a priority queue backed by a sorted vec where you only want to process items above a threshold:

1
2
3
4
5
6
7
8
9
let mut scores = vec![10, 25, 50, 75, 90];

let mut high_scores = Vec::new();
while let Some(score) = scores.pop_if(|s| *s >= 70) {
    high_scores.push(score);
}

assert_eq!(high_scores, vec![90, 75]);
assert_eq!(scores, vec![10, 25, 50]);

Clean, expressive, and no off-by-one errors. Another small addition to Vec that makes everyday Rust just a bit nicer.

43. Vec::extract_if — Remove Elements and Keep Them

Ever needed to split a Vec into two groups — the ones you keep and the ones you remove? retain discards the removed items. Now there’s a better way.

Vec::extract_if (stable since Rust 1.87) removes elements matching a predicate and hands them back as an iterator — in a single pass.

The old way — two passes, logic must stay in sync

1
2
3
4
5
6
7
8
9
let mut numbers = vec![1, 2, 3, 4, 5, 6];

// Collect the evens first…
let evens: Vec<i32> = numbers.iter().filter(|&&x| x % 2 == 0).copied().collect();
// …then remove them (predicate must match exactly)
numbers.retain(|&x| x % 2 != 0);

assert_eq!(numbers, [1, 3, 5]);
assert_eq!(evens,   [2, 4, 6]);

The filter and the retain predicates must be inverses of each other — easy to mistype, and you touch the data twice.

The new way — one pass, one predicate

1
2
3
4
5
6
let mut numbers = vec![1, 2, 3, 4, 5, 6];

let evens: Vec<i32> = numbers.extract_if(.., |&mut x| x % 2 == 0).collect();

assert_eq!(numbers, [1, 3, 5]);
assert_eq!(evens,   [2, 4, 6]);

extract_if walks the Vec, removes every element where the closure returns true, and yields it. The .. is a range — you can narrow it to only consider a slice of the vector.

Real-world example: draining a work queue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#[derive(Debug)]
struct Job { id: u32, priority: u8 }

let mut queue = vec![
    Job { id: 1, priority: 3 },
    Job { id: 2, priority: 9 },
    Job { id: 3, priority: 1 },
    Job { id: 4, priority: 8 },
];

// Pull out all high-priority jobs for immediate processing
let urgent: Vec<Job> = queue.extract_if(.., |j| j.priority >= 8).collect();

assert_eq!(urgent.len(), 2);  // jobs 2 and 4
assert_eq!(queue.len(),  2);  // jobs 1 and 3 remain

HashMap and HashSet also gained extract_if in Rust 1.88.

Note: The closure takes &mut T, so you can even mutate elements mid-extraction before deciding whether to remove them.

#008 Feb 2022

8. Drain

Use drain to remove specified range from a vector.

1
2
3
4
5
let mut a = vec![1,2,3,4,5];

let _ = a.drain(0..3);

assert!(a == vec![4,5]);

What is the difference to call into_iter which returns T ?

into_iter takes the collection by value and consumes it.

drain borrows mutable reference, and returns a drain iterator of elements. If such iterator is dropped without exhausting it, all elements are dropped.