Skip to content

Vec::as_mut_ptr

Vec methods · Collections

Level: reference · for working programmers

One line: The raw address of the buffer, writable.

pub const fn as_mut_ptr(&mut self) -> *mut T

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_mut_ptr in the standard library ↗