Foobar
Foobar

Reputation: 8487

Can I free memory from Vec::into_boxed_slice using Box::from_raw?

I saw the following code for returning a byte array to C:

#[repr(C)]
struct Buffer {
    data: *mut u8,
    len: usize,
}

extern "C" fn generate_data() -> Buffer {
    let mut buf = vec![0; 512].into_boxed_slice();
    let data = buf.as_mut_ptr();
    let len = buf.len();
    std::mem::forget(buf);
    Buffer { data, len }
}

extern "C" fn free_buf(buf: Buffer) {
    let s = unsafe { std::slice::from_raw_parts_mut(buf.data, buf.len) };
    let s = s.as_mut_ptr();
    unsafe {
        Box::from_raw(s);
    }
}

I notice that the free_buf function takes a Buffer, instead of a *mut u8. Is this intentional?

Can the free_buf function be reduced to:

unsafe extern "C" fn free_buf(ptr: *mut u8) {
    Box::from_raw(ptr);
}

Upvotes: 1

Views: 757

Answers (3)

Stargateur
Stargateur

Reputation: 26757

No, what you do is undefined behavior, it's mandatory that the type between into_raw() and from_raw() match. Rust alloc API doesn't require the allocator to remember any information, and so the allocation implementation will expect correctness of all information pass to it.

In your example, *mut u8 and *mut [u8] are a totally different type and so have different layout.

Also, mismatch the type could prevent destructor to run properly.

You can't use from_raw() to destruct any pointer like C free() using void *.

Upvotes: 0

harmic
harmic

Reputation: 30597

You are correct to note that the C runtime free function takes only a pointer to the memory region to be freed as an argument.

However, you don't call this directly. In fact Rust has a layer that abstracts away the actual memory allocator being used: std::alloc::GlobalAlloc.

The reason for providing such an abstraction is to allow other allocators to be used, and in fact it is quite easy to swap out the default OS provided allocator.

It would be quite limiting to require that any allocator keeps track of the length of blocks to allow them to be freed without supplying the length to the deallocation function, so the general deallocation function requires the length as well.

You might be interested to know that C++ has a similar abstraction. This answer provides some more discussion about why it could be preferable to require the application to keep track of the lengths of allocated memory regions rather than the heap manager.

Upvotes: 1

Ruifeng Xie
Ruifeng Xie

Reputation: 906

If we check the type of Box::from_raw, we see that it construct a Box<u8> from a raw *mut u8. One would need a *mut [u8] (fat pointer to slice) in order to construct a Box<[u8]> (which is what we have in the very beginning).

And dropping a Box<u8> will (at best) only release one byte of memory (if not causing a runtime error), while dropping a Box<[u8]> correctly releases all the memory.

Upvotes: 2

Related Questions