You want to copy buf[2..5] over buf[0..3] — same buffer, no allocation. Reach for copy_from_slice and the borrow checker says no. copy_within is the one-call answer.
The two-borrow trap
The obvious code can’t compile — buf would be borrowed mutably and immutably at the same time:
1
2
3
4
5
| let mut buf = [1, 2, 3, 4, 5];
// error[E0502]: cannot borrow `buf` as immutable
// while also borrowed as mutable
// buf[0..3].copy_from_slice(&buf[2..5]);
|
The usual workarounds are noisy. split_at_mut to carve the slice into two non-overlapping halves:
1
2
3
4
| let mut buf = [1, 2, 3, 4, 5];
let (left, right) = buf.split_at_mut(2);
left[0..2].copy_from_slice(&right[0..2]);
assert_eq!(buf, [3, 4, 3, 4, 5]);
|
…which only works when source and destination land on opposite sides of the split. Otherwise you allocate a throwaway Vec just to break the borrow:
1
2
3
| let mut buf = [1, 2, 3, 4, 5];
let tmp: Vec<_> = buf[2..5].to_vec(); // allocation just to copy
buf[0..3].copy_from_slice(&tmp);
|
copy_within is the primitive
<[T]>::copy_within(src, dest) copies a range of elements to a destination index inside the same slice — one call, no allocation, no split:
1
2
3
| let mut buf = [1, 2, 3, 4, 5];
buf.copy_within(2..5, 0);
assert_eq!(buf, [3, 4, 5, 4, 5]);
|
It’s memmove semantics, so overlapping source and destination just work — the elements that get overwritten don’t matter, the surviving order does:
1
2
3
4
| let mut buf = [1, 2, 3, 4, 5];
// shift everything one slot to the right
buf.copy_within(0..4, 1);
assert_eq!(buf, [1, 1, 2, 3, 4]);
|
Try writing that with split_at_mut — you can’t, the source and destination overlap.
A real shape: drop the first N from a Vec
Removing the first n elements without reallocating is copy_within plus a truncate:
1
2
3
4
5
6
7
8
| let mut data: Vec<u8> = vec![10, 20, 30, 40, 50];
let n = 2;
let len = data.len();
data.copy_within(n..len, 0);
data.truncate(len - n);
assert_eq!(data, [30, 40, 50]);
|
Same allocation, same backing buffer — the values just shift down. Vec::drain(..n) reads cleaner for one-offs, but copy_within is what you want when you’re already holding &mut [T] and can’t reach for Vec methods (think ring buffers, fixed-size scratch arrays, no_std crates).
Constraints
T: Copy is required — the method does a memmove, it doesn’t run destructors or call Clone. Source and destination ranges must both fit inside the slice; otherwise it panics. The destination is a single index (where the copy starts), not a range — the length is taken from the source range.
When to reach for it
Any time you’d otherwise write split_at_mut just to satisfy the borrow checker, or allocate a temporary buffer to break a self-borrow. copy_within reads as what you actually meant: move these bytes over there, in place.
Stable since Rust 1.37. Works on [T], Vec<T>, and any DerefMut<Target = [T]>.