Borrow-Checker

#230 Jun 2026

230. slice::split_at_mut — Two Mutable Halves, No unsafe, No Borrow Fight

Need a &mut to two different elements of the same slice at once? Indexing twice won’t compile — the borrow checker sees the whole slice borrowed twice. split_at_mut hands you two non-overlapping mutable halves instead.

The borrow checker says no

You want to mutate two elements of a slice in the same expression — say, swap-style logic that reads one and writes another:

1
2
3
4
let mut v = [1, 2, 3, 4];
let a = &mut v[0];
let b = &mut v[3]; // error: cannot borrow `v` as mutable more than once
*a += *b;

The compiler can’t prove v[0] and v[3] are different elements, so it rejects two overlapping &mut borrows of v. Both indexing operations borrow the entire slice.

split_at_mut gives you two disjoint slices

split_at_mut(mid) splits a slice into [0, mid) and [mid, len), returning a &mut to each. Because the halves provably don’t overlap, the borrow checker is happy:

1
2
3
4
let mut v = [1, 2, 3, 4];
let (left, right) = v.split_at_mut(2);
left[0] += right[1]; // v[0] += v[3]
assert_eq!(v, [5, 2, 3, 4]);

Internally it’s one bounds check and a pointer offset — no copying, no allocation. It panics only if mid > len; use split_at_mut_checked to get an Option instead.

Where it shines: in-place algorithms

Reversing a slice by hand needs a read and a write to two indices each step. Split once, walk inward:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn reverse<T>(s: &mut [T]) {
    let n = s.len();
    let (front, back) = s.split_at_mut(n / 2);
    for (a, b) in front.iter_mut().zip(back.iter_mut().rev()) {
        std::mem::swap(a, b);
    }
}

let mut data = [1, 2, 3, 4, 5];
reverse(&mut data);
assert_eq!(data, [5, 4, 3, 2, 1]);

The two iterators borrow disjoint halves, so iterating both mutably at once just works.

Divide and conquer for free

Because each half is itself a &mut [T], recursive algorithms split cleanly — the basis for parallelizing with something like Rayon’s join:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn sum_halves(s: &mut [i32]) -> i32 {
    if s.len() <= 1 {
        return s.first().copied().unwrap_or(0);
    }
    let mid = s.len() / 2;
    let (left, right) = s.split_at_mut(mid);
    sum_halves(left) + sum_halves(right)
}

let mut v = [1, 2, 3, 4, 5];
assert_eq!(sum_halves(&mut v), 15);

Whenever you’re fighting the borrow checker over two mutable spots in one slice, stop reaching for unsafesplit_at_mut is the safe, zero-cost answer. For non-adjacent individual elements, its cousin get_disjoint_mut does the same trick by index.

#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.

45. get_disjoint_mut — Multiple Mutable References at Once

The borrow checker won’t let you hold two &mut refs into the same collection — even when you know they don’t overlap. get_disjoint_mut fixes that without unsafe.

The problem

You want to update two elements of the same Vec together, but the compiler won’t allow two mutable borrows at once:

1
2
3
4
let mut scores = vec![10u32, 20, 30, 40];
let a = &mut scores[0];
let b = &mut scores[2]; // ❌ cannot borrow `scores` as mutable more than once
*a += *b;

The borrow checker doesn’t know indices 0 and 2 are different slots — it just sees two &mut to the same Vec. The classic escape hatches (split_at_mut, unsafe, RefCell) all feel like workarounds for something that should just work.

get_disjoint_mut to the rescue

Stabilized in Rust 1.86, get_disjoint_mut accepts an array of indices and returns multiple mutable references — verified at runtime to be non-overlapping:

1
2
3
4
5
6
7
let mut scores = vec![10u32, 20, 30, 40];

if let Ok([a, b]) = scores.get_disjoint_mut([0, 2]) {
    *a += *b; // 10 + 30 = 40
}

assert_eq!(scores, [40, 20, 30, 40]); // ✅

The Result is Err only if an index is out of bounds or indices overlap. Duplicate indices are caught at runtime and return Err — no silent aliasing bugs.

Works on HashMap as well

HashMap gets the same treatment. The return type is [Option<&mut V>; N] — one Option per key, since keys can be missing:

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

let mut accounts: HashMap<&str, u32> = HashMap::from([
    ("alice", 100),
    ("bob", 200),
]);

// Transfer 50 from bob to alice
let [alice, bob] = accounts.get_disjoint_mut(["alice", "bob"]);
if let (Some(a), Some(b)) = (alice, bob) {
    *a += 50;
    *b -= 50;
}

assert_eq!(accounts["alice"], 150);
assert_eq!(accounts["bob"], 150); // ✅

Passing duplicate keys to the HashMap version panics — the right tradeoff for a bug that would otherwise silently produce undefined behavior.

When to reach for it

  • Swapping or combining two elements in a Vec without split_at_mut gymnastics
  • Updating multiple HashMap entries in one pass
  • Any place you’d have used unsafe or RefCell just to hold two &mut into the same container

If your indices or keys are known not to overlap, get_disjoint_mut is the clean, safe answer.