How to get a raw pointer to a Rust heap array?

I have this struct:

    struct CBuffer {
        ptr: *mut u8,
        len: usize,
    }

that is supposed to store buffers from C side. I do not have the C code working yet so I want to simulate a buffer coming from C.

let packet1 = [69, 0, 0, 72, 203, 203, 64, 0];

I can get a pointer to it by doing let p = &packet1 as *const u8; however, since this array is stack allocated, if I put p inside the buffer, things might not go well, since this array will stop existing they it goes out of scope. So I guess the struct will hold invalid data.

I've found that I can alocate arrays dynamically with Vec, like this:

let mut a = Vec::with_capacity(1000);
for i in 0..1000 {
    a.push(i as f64);
}

but can I trust that this Vec can be converted to a raw buffer of u8? I don't think so, since it's a struct that have things like size and other properties so it has more data than a simple u8 buffer.

So how to allocate a C-like buffer in the heap in Rust?

Vec has the as_ptr/as_mut_ptr function. It's not safe to simply cast it, it contains pointer, length and capacity fields internally.

If you want to cast a slice instead you can do slice.len(), and slice as *const [_] as *const _ as *const u8, and this is safe, but endianness may be an issue if your C library is transferring data.

More specifically it's not safe to cast the Vec directly, since it's repr(Rust) (implicitly, so your CBuffer is wrong too if you're going to send it across). So the order isn't specified and may change without notice.

The only difference between the array and the Vec is that one is on the stack and the other is on the heap, but if either is dropped the data will be invalidated. Note however, that if the C library requires your data to be on the heap for some reason, you should use the Vec or even a Box<[T]>.

Incidentally, that is the exact same method that I already described in my answer to your previous post.

Unrelated, but your code

let mut a = Vec::with_capacity(1000);
for i in 0..1000 {
    a.push(i as f64);
}

can be simplified to

let v: Vec<_> = (0..1000).collect(); // defaults to i32

(playground)

where _ can be replaced with an integer type; or, if f64 is necessary,

let v: Vec<_> = (0..1000).map(f64::from).collect();

(playground)

1 Like