Rust跨语言库字符串内存双端安全释放实现咨询
解决方案:区分所有权实现跨场景内存管理
完全可行,不用在Rust端手动调用free函数,核心思路是给结构体添加所有权标记,区分内存是由Rust自动管理还是交由C/C++手动释放,从而避免双free或内存泄漏问题。
问题根源
你之前触发双free的原因是:结构体里的裸指针没有所有权标记,Drop trait和C端的free_shared_string都尝试释放同一块内存;而不加Drop时,Rust端无法自动回收into_raw()转出的内存,导致泄漏。
具体实现
第一步,给结构体添加所有权标记,并加上#[repr(C)]确保C语言能正确解析结构体布局(FFI必备):
use std::{ffi::{CString, CStr}, os::raw::c_char}; #[repr(C)] pub struct SomeData { pub data: *const c_char, owned: bool, // 标记内存是否由Rust拥有所有权 }
第二步,实现Drop trait,仅当Rust拥有所有权时才释放内存:
impl Drop for SomeData { fn drop(&mut self) { // 只有当Rust拥有所有权且指针非空时,才回收内存 if self.owned && !self.data.is_null() { unsafe { // from_raw会重新获取CString的所有权,drop时自动释放内存 let _ = CString::from_raw(self.data.cast_mut()); } } } }
第三步,区分构造函数:给Rust内部使用的构造函数设置owned: true,给FFI提供的构造函数设置owned: false(所有权转移给C端):
impl SomeData { // Rust内部使用的构造函数,自动管理内存 pub fn new(s: &str) -> Self { let c_str = CString::new(s).expect("字符串包含空字节"); SomeData { data: c_str.into_raw(), owned: true, } } // FFI构造函数:接收C字符串,返回结构体指针,所有权转移给C端 #[no_mangle] pub extern "C" fn some_data_new(s: *const c_char) -> *mut SomeData { let rust_str = unsafe { CStr::from_ptr(s).to_str().expect("无效的C字符串") }; let c_str = CString::new(rust_str).expect("字符串包含空字节"); let data = c_str.into_raw(); // 用Box包装后转成裸指针,C端拿到后需要手动释放结构体本身 Box::into_raw(Box::new(SomeData { data, owned: false })) } }
第四步,修正FFI的free函数,确保同时回收字符串内存和结构体本身:
#[no_mangle] pub extern "C" fn free_shared_string(ptr: *mut SomeData) { if ptr.is_null() { return; } // 从裸指针恢复Box,确保结构体本身被正确释放 let mut obj = unsafe { Box::from_raw(ptr) }; if !obj.data.is_null() { unsafe { let _ = CString::from_raw(obj.data.cast_mut()); } obj.data = std::ptr::null(); // 避免悬空指针 } }
使用说明
- Rust端:直接用
SomeData::new("xxx")创建,无需手动释放,Drop会自动回收内存。 - C/C++端:通过
some_data_new创建结构体指针,使用完后调用free_shared_string释放内存(包括字符串和结构体本身)。
这样既解决了Rust端的内存泄漏问题,又避免了C端和Rust端的双free冲突。
内容的提问来源于stack exchange,提问作者t348575
相关产品推荐
相关产品推荐

