Why the addresses are same?

See the playground:

use rand::Rng;

fn main() {
    let mut data: Vec<*const u8> = Vec::new();

    for _ in 0..5 {
        let mut num: Vec<u8> = Vec::new();
        for _ in 0..16 {
            let rand_num: u8 = rand::thread_rng().gen();
            num.push(rand_num)
        }
        println!("num is : {:?},num.as_ptr(): {:?}", num, num.as_ptr());
        data.push(num.as_ptr());
    }

    println!("data is: {:?}", data);
}

Output:

num is : [115, 9, 190, 41, 30, 145, 128, 236, 142, 72, 240, 195, 214, 151, 9, 56],num.as_ptr(): 0x56330649ab80
num is : [10, 50, 15, 18, 31, 157, 18, 191, 208, 180, 224, 240, 82, 110, 79, 222],num.as_ptr(): 0x56330649ab80
num is : [239, 194, 216, 2, 198, 65, 189, 31, 146, 16, 181, 40, 154, 240, 186, 211],num.as_ptr(): 0x56330649ab80
num is : [57, 238, 33, 9, 181, 83, 230, 200, 185, 184, 196, 102, 4, 218, 57, 141],num.as_ptr(): 0x56330649ab80
num is : [131, 44, 55, 233, 162, 115, 243, 237, 170, 201, 93, 161, 31, 192, 147, 0],num.as_ptr(): 0x56330649ab80
data is: [0x56330649ab80, 0x56330649ab80, 0x56330649ab80, 0x56330649ab80, 0x56330649ab80]

Why the addresses are all same?

I altered it:

use rand::Rng;

fn main() {
    let mut data :Vec<* const [u8]> = Vec::new();

    for _i in 0..5 {
        let mut num: Vec<u8>= Vec::new();
        for _j in 0..16 {
            let rand_num :u8 = rand::thread_rng().gen();
            num.push(rand_num);
        }
        println!("num({:?}/{:p}) is : {:?}", num.as_ptr(), &*num, num);
        let boxed = num.into_boxed_slice();
        data.push(Box::into_raw(boxed) as _);
    }
    
    println!("data is: {:?}",data);
}

It seems the Box::into_raw is the key?

At the end of the outer for loop, num is dropped, and the buffer it allocated is reused for the next Vec

If we unroll the loop,

let mut data: Vec<*const u8> = Vec::new();

// Iteration 1
{
    // `Vec::new` won't allocate immediately, ..
    let mut num_1: Vec<u8> = Vec::new();
    for _ in 0..16 {
        let rand_num: u8 = rand::thread_rng().gen();
        // .. but once a value is pushed, it will allocate a buffer 
        // on the heap to store its contents
        num_1.push(rand_num)
    }

    // Printing `num_1` will still show its contents after it's dropped
    println!("num is : {:?},num_1.as_ptr(): {:?}", num_1, num_1.as_ptr());
    data.push(num_1.as_ptr());
} // Here, `num_1` is dropped, and frees its buffer. 
  // Note that this invalidates the pointer pushed to `data`

// Iteration 2
{
    let mut num_2: Vec<u8> = Vec::new();
    for _ in 0..16 {
        let rand_num: u8 = rand::thread_rng().gen();
        // Once `num_2` allocates, the allocator just reuses the buffer `num_1` 
        // used, since that's free now. That's why they have the same pointer.
        // This doesn't make the pointer in `data` valid, though.
        num_2.push(rand_num)
    }
    println!("num is : {:?},num_2.as_ptr(): {:?}", num_2, num_2.as_ptr());
    data.push(num_.as_ptr());
}

// the rest of the iterations look just like iteration 2,
// all reusing the buffer `num_1` allocated

// Since all of the `Vec`s have been dropped, none of the pointers
// in `data` are valid to dereference (but printing is fine, since that
// just prints its address)
println!("data is: {:?}", data);

Using Box::into_raw doesn't deallocate the buffer (wouldn't be very useful if it did), so the allocator can't just reuse it for subsequent vectors and has to allocate a whole new one. In that case, that means the pointers in data are valid to dereference. Doing std::mem::forget(num) will also accomplish this, by preventing the Vec from being dropped

4 Likes

from another perspective , no one take the ownership of num, the above one is not.

yep

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.