go与rust字符串ffi必须严格遵循所有权规则:go传字符串需c.cstring()分配并配对c.free();rust接收后不可保存指针,需立即转换或自行拷贝;rust返回字符串须用cstring::new().into_raw()移交内存,并提供专用释放函数。

Go 侧直接传 *C.char 给 Rust 函数是危险的——Rust 无法自动管理 Go 分配的内存,且 Go 字符串底层是只读 slice,C.CString() 创建的副本若未配对 C.free() 就会泄漏。
Go 调用 Rust 时,字符串传入必须显式分配 + 显式释放
Go 中的 string 是不可变的、非 null 终止的 header+data 结构。Rust FFI 接口只能接收 C 兼容的 *const i8(即 const char*),因此必须走 C.CString() → 传指针 → Rust 处理 → Go 主动 C.free() 这条链。
-
C.CString()在 C 堆上分配内存并拷贝内容,返回*C.char;它不负责释放,你得自己记着调C.free() - Rust 端收到
*const i8后,应立即用std::ffi::CStr::from_ptr()构造视图,**不能保存该指针用于后续异步使用**(Go 可能在任意时刻C.free()) - 若 Rust 需要长期持有字符串内容,应由 Rust 自行
CString::new()拷贝一份,或让 Go 提供长度参数并配合std::slice::from_raw_parts()安全读取
Rust 返回字符串给 Go 时,必须移交所有权并提供释放函数
Rust 不能直接返回 &str 或 String——它们生命周期绑定在 Rust 栈或堆上,Go 无法安全访问。正确做法是:Rust 分配内存、写入 null 终止字符串、移交裸指针,并导出配套释放函数。
- Rust 端用
CString::new().unwrap().into_raw()获取*mut c_char,该指针所有权转交给 Go - Go 侧接收后,用
C.GoString()安全转换为 Gostring(它会自动拷贝并以 null 截断) - 但若 Go 需要保留原始 C 字符串(比如传给另一个 C 函数),则必须在不再需要时调用 Rust 导出的
free_c_string(ptr),该函数内调用CString::from_raw()归还内存 - 切勿在 Go 中对 Rust 返回的指针调用
C.free()——它不是C.malloc()分配的,行为未定义
常见崩溃点:null 字节、悬垂指针与类型误判
三类错误最常导致段错误或静默数据损坏:
-
CString::new()在 Rust 端失败:如果 Go 传入的字符串含\0,CString::new()会 panic(默认 abort)。应在 Rust 入口加if input.is_null() { return ptr::null_mut(); }并在 Go 侧检查返回值是否为空 - Go 忘记
C.free():每次C.CString()都对应一次 C 堆分配,漏掉就内存泄漏;可用defer C.free(unsafe.Pointer(cstr))强制配对 - 误把
*C.char当作 Go 字符串直接取地址:例如(*[1]byte)(unsafe.Pointer(cstr))[:len]是错的——cstr是 C 堆指针,Go 不知道其长度,必须由 Rust 同时返回长度,或依赖 null 终止
真正难处理的不是“怎么传”,而是“谁在什么时候释放哪块内存”。只要所有权移交路径清晰(Go 分配 → Go 释放;Rust 分配 → Rust 释放函数回收),再配上 null 检查和长度验证,就能避开绝大多数崩溃。其余细节,比如字节序或对齐,对字符串本身影响不大——它们只作用于结构体字段排列。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











