Rust FFI场景下如何用*mut指针与长度正确释放数组?
请勿将此问题标记为无std下将*mut u8转为&[u8]的重复问题,我需要对自己的实现思路进行评审。
问题背景
考虑如下C++代码,释放指针数组需要执行:
void foo() { T ** array_of_pointers = ...; size_t size_of_array = ...; for (int i = 0; i < size_of_array; i++) { delete array_of_pointers[i]; } delete [] array_of_pointers; }
在Rust中我也构建了类似的array_of_pointers结构,用于模拟FFI代码场景(具体细节已省略以简化问题)。
我的实现代码
定义了RawCppPtr作为T*(非数组)的包装,RawCppPtrArr作为T**(T*的数组)的包装。此处假设T为String,通过gen_cpp_string创建String并放入Vec<RawCppPtr>,再将其转为*mut [RawCppPtr](包含数组长度的胖指针),最终转为*mut RawCppPtr(指向数组首元素的瘦指针,是否正确?)。
pub struct RawCppPtr { pub ptr: *mut std::os::raw::c_void, pub type_: u64, } impl Drop for RawCppPtr { fn drop(&mut self) { assert_eq!(self.type_, 1); unsafe { drop(Box::<Vec<u8>>::from_raw(self.ptr as *mut Vec<u8>)); } } } extern "C" fn gen_cpp_string(s: &[u8]) -> RawCppPtr { let str = Box::new(Vec::from(s)); let ptr = Box::into_raw(str); RawCppPtr { ptr: ptr as *mut _, type_: 1, } } unsafe impl Send for RawCppPtrArr {} pub struct RawCppPtrArr { pub inner: *mut RawCppPtr, pub len: u64, } impl Drop for RawCppPtrArr { fn drop(&mut self) { unsafe { let len = self.len; // Delete all `T**` for i in 0..len { let i = i as usize; let inner_i = self.inner.add(i); drop(inner_i); } // Delete `T*` drop(self.inner); self.inner = std::ptr::null_mut(); self.len = 0; } } } fn main() { let len = 10; let mut v: Vec<RawCppPtr> = vec![]; for i in 0..len { let s = format!("s{}", i); v.push(gen_cpp_string(s.as_bytes())); } let box_v = v.into_boxed_slice(); // Convert from *mut [RawCppPtr] tp *mut RawCppPtr let ptr_va = Box::into_raw(box_v); println!("va {}", std::mem::size_of_val(&(ptr_va))); // 16 which means a fat pointer let ptr_v = ptr_va as *mut RawCppPtr; let cpp_ptr_arr = RawCppPtrArr { inner: ptr_v, len: len as u64, }; println!("v {}", std::mem::size_of_val(&(ptr_v))); // 8 which means a thin pointer drop(cpp_ptr_arr); }
当前问题与疑问
我认为当前代码存在内存泄漏:所有String已被释放,但RawCppPtr数组未被正确释放,应该释放*mut [RawCppPtr]而非*mut RawCppPtr来释放该数组。
我想了解如何通过*mut RawCppPtr与原*mut [RawCppPtr]的长度来正确释放此数组。注意:由于是FFI代码,C++没有胖指针,因此我不想使用*mut [RawCppPtr]。
更新与补充
经过搜索,我发现from_raw_parts系列函数可能有用,但不确定这是否是最佳实践,同时也需要对我的整体实现思路进行评审。
补充FFI场景说明:在我的程序中,Rust知道何时释放RawCppPtr,但不知道如何释放(因为它由C创建),因此Rust释放RawCppPtr时会调用FFI函数,由C端通过类型检查来完成释放。例如RawCppPtr作为Rust持有的上下文,但仅对C有意义,当Rust不再需要调用C时就会释放它。
内容的提问来源于stack exchange,提问作者calvin

