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?
shrink_to_fit
makeslen
andcapacity
equal. In any case, the problem you want to solve (how to give ownership of a vector to C code) seems to be the same as that question, so either the answers there are wrong or they should be helpful. – LeptophyllousVec::into_boxed_slice
which "will drop any excess capacity", then obtain your raw pointer withBox::into_raw
and later reconstruct withstd::ptr::slice_from_raw_parts_mut
followed byBox::from_raw
? – AfraidVec
again, if required, with<[T]>::into_vec
). – AfraidVec
itself acts is if the new capacity is the same as the old, and I don't even see how the allocator could inform it of excess allocated capacity. I encourage you to create a thread on IRLO to change that. Anyway, you canassert_eq!(vec.capacity(), vec.len())
but I don't see a way you can enforce it. – Compeer