Prepending to a Vec in Rust: What Are Your Options?
Rust’s Vec is a powerful and flexible collection type. It’s fast at pushing and popping at the end, but sometimes you need to add an element to the front. That’s where things get tricky: Vec is backed by a contiguous array, so prepending requires shifting or reallocating elements. There’s no O(1) way to do this.
In this post, we’ll walk through several ways to prepend to a Vec, compare their mechanics, and discuss when each is appropriate.
The Problem
You have a vector:
let mut digits = vec![2, 3, 4];
You want to add 1 to the head, so that the result is [1, 2, 3, 4].
Option 1: Using insert(0, value)
The simplest and most idiomatic solution:
digits.insert(0, 1);
-
Shifts all elements one place to the right.
-
Uses
ptr::copyunder the hood (a singlememmove). -
Complexity: O(n).
This is usually the best choice unless you need to squeeze out every last cycle.
Option 2: Build a New Vec With Capacity
If you’re fine creating a new vector:
let mut result = Vec::with_capacity(digits.len() + 1);
result.push(1);
result.extend(digits);
-
Allocates a new buffer sized exactly for the result.
-
Copies all elements once into the new buffer.
-
Useful when you’re already constructing a fresh vector.
Option 3: Manual Shifting With copy_within
You can explicitly shift elements forward:
digits.push(0); // grow by one
let len = digits.len();
digits.copy_within(0..len-1, 1);
digits[0] = 1;
-
Performs the same work as
insert, but with more control. -
Complexity: O(n).
-
Sometimes avoids an extra bounds check.
Option 4: Unsafe Pointer Manipulation
For maximum control, you can use raw pointers:
digits.reserve(1);
let len = digits.len();
unsafe {
std::ptr::copy(
digits.as_ptr(),
digits.as_mut_ptr().add(1),
len,
);
std::ptr::write(digits.as_mut_ptr(), 1);
digits.set_len(len + 1);
}
-
Essentially reimplements what
insertdoes internally. -
No additional allocations if capacity is sufficient.
-
Only worth it for performance-critical hot paths.
Option 5: Reverse, Push, Reverse
A creative trick:
digits.reverse();
digits.push(1);
digits.reverse();
-
Two reversals = O(n).
-
Works, but less idiomatic.
-
Sometimes useful if you’re building vectors backwards.
Summary
-
Use
insert(0, value)if you just need to prepend occasionally. It’s clean and efficient enough. -
Use
with_capacity+push+extendif you’re already making a new vector. -
Use manual shifting or unsafe pointer manipulation only if you’re in a hot loop and need the absolute fastest approach.
-
Reverse, push, reverse is a neat trick, but rarely preferable.
At the end of the day, all these options are O(n). The right choice depends less on theoretical speed and more on code clarity and allocation patterns.

Comments
Post a Comment