Vec::as_mut_ptr¶
Level: reference · for working programmers
One line: The raw address of the buffer, writable.
Stable since 1.37.0. Callable in a const context since 1.87.0.
Same pointer as as_ptr with write permission, and every caveat carries over: unsafe to use, invalidated by any reallocation, and not borrow-checked, so a stale one is undefined behaviour rather than a compile error.
The pattern it exists for is the fill-and-report shape of a C API: hand out the pointer and the capacity, let the callee write, then set_len to whatever it says it wrote.
spare_capacity_mut is the better-typed door to the same region — it gives &mut [MaybeUninit<T>], which carries a length and cannot be pointed past by accident.
When you control the writer, extend, resize and extend_from_slice do the same job with no unsafe. first_mut, last_mut, get_mut and iter_mut cover the borrow-checked cases.
Example¶
vec_as_mut_ptr.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
fn main() {
let mut v = vec![1u32, 2, 3];
let p = v.as_mut_ptr();
unsafe { *p = 99; *p.add(2) = 77; }
println!("{v:?}");
// Same pointer as as_ptr, with write permission — and the same rule:
// it is invalidated by anything that can reallocate.
let mut v: Vec<u8> = Vec::with_capacity(4);
v.push(1);
let before = v.as_mut_ptr() as usize;
v.push(2);
println!("still within capacity: {}", v.as_mut_ptr() as usize == before);
// The pattern it exists for: hand a buffer to something that fills it,
// then set the length to what was actually written.
fn fill(ptr: *mut u8, cap: usize) -> usize {
let n = cap.min(4);
for i in 0..n { unsafe { *ptr.add(i) = (i as u8 + 1) * 11 } }
n // how many it wrote
}
let mut buf: Vec<u8> = Vec::with_capacity(8);
let written = fill(buf.as_mut_ptr(), buf.capacity());
unsafe { buf.set_len(written) };
println!("filled through a raw pointer: {buf:?}");
// The safe spelling of the same thing, when you control the writer.
let mut buf: Vec<u8> = Vec::with_capacity(8);
buf.extend((1..=4).map(|i| i * 11));
println!("the safe version: {buf:?}");
// Taking it does not borrow the Vec for any length of time, which is the
// trap: the compiler will NOT stop you using a stale pointer.
let mut v = vec![1u8];
let p = v.as_mut_ptr();
unsafe { *p = 2 }; // fine: nothing has reallocated yet
v.reserve(1000); // p may now be dangling
println!("use `p` after this and it is UB, not a compile error: {v:?}");
// Which is why the borrow-checked route is the default answer.
let mut v = vec![1, 2, 3];
if let Some(first) = v.first_mut() { *first = 9; }
println!("{v:?}");
}
Verified output of vec_as_mut_ptr.rs — regenerated by tools/run_examples.py, never hand-typed.
[99, 2, 77]
still within capacity: true
filled through a raw pointer: [11, 22, 33, 44]
the safe version: [11, 22, 33, 44]
use `p` after this and it is UB, not a compile error: [2]
[9, 2, 3]
See also¶
Vec::as_ptr— the read-only versionVec::spare_capacity_mut— the same region, better typedVec::set_len— the call that makes written bytes into elementsVec::as_mut_slice— the safe writable view