I have a Rust function that passes a byte array to C#:
#[no_mangle]
pub extern "C" fn get_bytes(len: &mut i32, bytes: *mut *mut u8) {
let mut buf : Vec<u8> = get_data();
buf.shrink_to_fit();
// Set the output values
*len = buf.len() as i32;
unsafe {
*bytes = buf.as_mut_ptr();
}
std::mem::forget(buf);
}
From C#, I can call it without crashing. (In lieu of a crash, I assume this is correct but am not 100% sure):
[DllImport("my_lib")] static extern void get_bytes(ref int len,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref byte[] bytes);
void test()
{
int len = 0;
byte[] bytes = null;
get_bytes(ref len, ref bytes);
}
I then make use of bytes, but I understand that this memory needs to be freed by Rust. So I have another Rust function to free it:
#[no_mangle]
pub extern "C" fn free_bytes(len: i32, bytes: *mut *mut u8) {
// also tried with: -------------- bytes: *mut u8
assert!(len > 0);
// Rebuild the vec
let v = unsafe { Vec::from_raw_parts(bytes, len as usize, len as usize) };
//println!("bytes to free: {:?}", v);
drop(v); // or it could be implicitly dropped
}
And the corresponding C#. Making the call crashes my app:
[DllImport("my_lib")] extern void free_bytes(int len, ref byte[] bytes);
void test()
{
int len = 0;
byte[] bytes = null;
get_bytes(ref len, ref bytes);
// copy bytes to managed memory
bytes[] copy = new byte[len];
bytes.CopyTo(copy, 0);
// free the unmanaged memory
free_bytes(len, ref bytes); // crash occurs when executing this function
}
I see that Vec::from_parts_raw "is highly unsafe". Because "capacity needs to be the capacity that the pointer was allocated with.", I also tried passing the capacity between Rust & C# without shrink_to_fit to preserve length & capacity. That also crashed.
I assume from_parts_raw recovers the existing memory on the heap, but I noticed that the byte content in C# (shown in Visual Studio) doesn't match the contents in Rust (via the 'bytes to free' println). So is my error in how I'm recovering the Vec<u8> to be freed, in the types Rust is accepting (eg, *mut u8 vs *mut *mut u8), in my C# DllImport, somewhere else?