Search code examples
rustunsafe

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);
}

Solution

  • 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.