I have a library which uses another C library underneath via FFI, and the library has a function which allocates some data (just a sequence of bytes) and returns a pointer to the data (along with the allocation's size). And I somehow need to construct a "Rust-native" Vec<u8> out of this data.
My intent is to use the Vec<T>: From<&[T]> implementation, but for that I have to construct a slice somehow. So, is the following considered UB, and are there better approaches?
extern "C" {
fn get_data(ptr: *mut *mut u8, size: *mut usize);
}
fn main() {
let ptr: *mut *mut u8 = std::ptr::null_mut();
let size = 0;
unsafe {
get_data(&raw mut ptr, &raw mut size);
}
// data pointed by `ptr` is allocated using `malloc`, though we never drop it
let slice: &[u8] = unsafe { std::mem::transmute((ptr, size)) };
// Construct the vector by copying each element
let v = Vec::from(slice);
}
Thanks in advance!
Edit: The data returned by get_data is now owned by the caller, and is later free'd
Vec::from(slice) creates a new Vec and copies the data into it. If i understand you correctly you probably want Vec::from_raw_parts as that also deallocates the memory when you are done with it.
Vec deallocates using Rust's allocator, which cannot be used to free C allocations. This is UB, but it might happen to work correctly sometimes.
In general, the same language that allocated some memory must be the one to free it. OP, what does the C library's documentation say about freeing the data given from get_data?[1] Is it giving the data up entirely to the caller, or is it just temporarily borrowed from C?
C's free function is easily accessible from Rust under libc::free. âŠī¸
you can use a custom Vec like (actually, more Box<[u8]> like) type which Deref to [u8], but you cannot use the FFI pointer to call Vec::from_raw_parts(), because Vec::from_raw_parts() must use a pointer allocated by rust's Global allocator.
however, you can create a slice from FFI the pointer directly, since the element type u8 is FFI safe. Vec::from_slice() is safe, but it will allocate memory from rust's global allocator and copy the data.
It's definitely not UB to construct a slice out of data allocated by C. You can do so using std::slice::from_raw_parts, which takes a pointer and a length.
However, this is definitely not okay. There's no guarantee that the layouts match here. You need to use slice::from_raw_parts instead.
As long as you're okay with copying the elements over, then sure. You cannot create a vector without copying, though, because the vector would use the wrong destructor to free the data in its destructor / when it reallocates.