Safety of vec set_len

Hi,

The documentation of set_len(&mut self, new_len: usize) say that:

  • new_len must be less than or equal to capacity() .
  • The elements at old_len..new_len must be initialized.

If, the memory has been allocated but not initialized, then I set the len with set_len, and then I initialize each new element shouldn't it be safe? There is any UB in doing the below?

fn main() {
  let mut rng = rand::thread_rng();
  
  let mut g: Vec<u8> = Vec::with_capacity(100);
  
  unsafe { g.set_len(100) };
  
  for i in 0..100 {
      let v: u8 = rng.gen();
      g[i] = v;
  }
  
  dbg!(g.get(56));
}

The above and the below are equivalent?

fn main() {
    let mut rng = rand::thread_rng();

    let mut g: Vec<MaybeUninit<u8>> = unsafe {
        vec![MaybeUninit::uninit().assume_init(); 100]
    };
    
    for elem in &mut g[..] {
        let v: u8 = rng.gen();
        *elem = MaybeUninit::new(v);
    }
    
    let g = unsafe { std::mem::transmute::<_, Vec<u8>>(g) };
    dbg!(g[57]);
}

Are the aboves faster than the below?

fn main() {
    let mut rng = rand::thread_rng();
    let mut g = vec![0_u8;100];
    for x in &mut g {
        let v: u8 = rng.gen();
        *x = v
    }
    dbg!(g[57]);
}

thanks!

You are supposed to first initialize the memory, then call set_len.

What about the second option? There are any particular differences of safety and performance between 1 2 and 3? Ty

The first two violate the safety rules, so they are out immediately.

Anyway to safely get an uninitialized allocated buffer and then initialize each value separately?

This is safe:

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();

    let mut g: Vec<u8> = Vec::with_capacity(100);

    let ptr = g.as_mut_ptr();
    for i in 0..100 {
        let v: u8 = rng.gen();
        unsafe {
            std::ptr::write(ptr.add(i), v);
        }
    }

    unsafe { g.set_len(100) };

    dbg!(g.get(56));
}

Or much simpler:

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();

    let mut g: Vec<u8> = Vec::with_capacity(100);

    for i in 0..100 {
        let v: u8 = rng.gen();
        g.push(v);
    }

    dbg!(g.get(56));
}
1 Like

Sorry my fault I took a too simplistic example. What I need is to pass an &mut [u8] to a function so I need to set the len of the vector an then take a mutable slice to it and pass it to a function that will substitute each element of the slice so I think that the only viable options (from my first post) are 1 and 3. So if 1 in not doable because I need to initialize each element before set the len it remain only 3. I'm wondering if 1 is really not an option, or if maybe adding some tests I can consider it safe and if there is another way to reach the goal without initialize the elements? Ty

I actually control the function that write on the mutable slice, but I need it to be generic over the input so it take an AsMut[u8] is for that that I need a mutable slice and I can not just pass the vector and push into it

It is generally not safe to create an &mut [u8] to uninitiailized memory.

2 Likes

It is always UB to create an &mut [u8] to uninitialized memory.

https://doc.rust-lang.org/reference/behavior-considered-undefined.html

2 Likes

Ok, thanks to everyone.