According the documentation for std::Vec
, calling shrink_to_fit()
will cause the Vec
's capacity to "drop down as close as possible to the length but the allocator may still inform the vector that there is space for a few more elements." Vec::with_capacity()
and Vec::reserve_exact()
each have a similar note saying that the reserved capacity may still be slightly greater than the length.
Meanwhile, the documentation for std::alloc::GlobalAlloc::dealloc()
states that the layout used to deallocate a block of memory "must be the same layout that was used to allocate that block of memory," which means the layout that is passed to dealloc()
needs to have the exact size of the block.
I am working on an FFI function that returns a list and a size. The C code that calls the function will have to call a free_X()
function I provide to deallocate the list. To do that, it passes in a pointer to the list and the size of the list. In Rust, I am using a std::Vec
for the list and want to shrink it so capacity == length
and then std::mem::forget()
it and return a pointer to it. The C code will pass in a pointer to a size_t
that I will set to the size. Here are examples of what the function signatures will look like in C:
List *obtain_list(size_t *size);
void free_list(List *list, size_t size);
You can probably see the dilemma. I can shrink the std::Vec
with shrink_to_fit()
, but my_vec.len()
might not equal my_vec.capacity()
. Thus, if C passes the size
it got from Rust to free_list()
, free_list()
will create a std::alloc::Layout
that doesn't match the allocated block's size (because the block size was my_vec.capacity()
, not my_vec.len()
). This could result in undefined behavior, per std::alloc::dealloc()
's documentation.
I could return the capacity of the list by changing the function signatures to pass the capacity to C, like so:
List *obtain_list(size_t *size, size_t *capacity);
void free_list(List *list, size_t size, size_t capacity);
I don't like having multiple pointers that are supposed to be initialized by the called function, so I'd probably create a struct instead that holds the list pointer as well as the size and capacity.
That seems hairy to me. I would much rather just return a size. Is there a way to force std::Vec
to reallocate its buffer to be exactly the same as the length?
Convert your Vec<T>
into a Box<[T]>
with Vec::into_boxed_slice()
. A boxed slice has no capacity, only a length, which is exactly the information you want to pass to C and back, so you won't need anything extra to deallocate it.