rust语言String转C语言原生*mut c_char
我就直接上菜吧:
/// Convert a String to C-compatible string /// /// This function will consume the provided data and use the underlying bytes to construct a new /// string, ensuring that there is a trailing 0 byte. This trailing 0 byte will be appended by this /// function; the provided data should *not* contain any 0 bytes in it. /// /// Returns a valid pointer, or NULL pub fn rust_string_to_c(s: String) -> *mut c_char { CString::new(s) .map(|c_str| c_str.into_raw()) .unwrap_or(std::ptr::null_mut()) } /// Free a CString allocated by Rust (for ex. using `rust_string_to_c`) /// /// # Safety /// /// s must be allocated by rust, using `CString::new` #[no_mangle] pub unsafe extern "C" fn rs_cstring_free(s: *mut c_char) { if s.is_null() { return; } drop(CString::from_raw(s)); }
要将 Rust 的
String 转换为 C 语言原生的 *mut c_char 指针,需要使用 std::ffi::CString 和一些 unsafe 代码。这个过程涉及所有权转移和手动内存管理,是 Rust FFI(外部函数接口)的核心部分。核心步骤
转换为 CString: 确保字符串以 C 语言要求的空字符(\0)结尾,并且内部不含空字符。
获取裸指针: 使用 into_raw() 方法,它会消耗 CString 的所有权,返回一个 *mut c_char 指针。
内存管理(关键): 您的 C 语言代码现在拥有这块内存。您必须提供一个函数,以便 C 语言在使用完毕后将所有权交还给 Rust,由 Rust 负责释放内存。
完整示例代码
这是一个完整的 Rust 模块示例,包含了分配和释放内存所需的函数:
use std::ffi::CString; use std::os::raw::c_char; // C 语言的 char 类型别名 /// Convert a String to C-compatible string /// /// This function will consume the provided data and use the underlying bytes to construct a new /// string, ensuring that there is a trailing 0 byte. This trailing 0 byte will be appended by this /// function; the provided data should *not* contain any 0 bytes in it. /// /// Returns a valid pointer, or NULL pub fn rust_string_to_c(s: String) -> *mut c_char { CString::new(s) .map(|c_str| c_str.into_raw()) .unwrap_or(std::ptr::null_mut()) } /// Free a CString allocated by Rust (for ex. using `rust_string_to_c`) /// /// # Safety /// /// s must be allocated by rust, using `CString::new` #[no_mangle] pub unsafe extern "C" fn rs_cstring_free(s: *mut c_char) { if s.is_null() { return; } drop(CString::from_raw(s)); } // 这是一个使用 #[no_mangle] 标记的函数,可以被外部 C 代码调用 /*#[no_mangle] pub extern "C" fn allocate_c_string(s: String) -> *mut c_char { // 1. 将 Rust String 转换为 CString let c_string = CString::new(s).expect("String 不能包含内部的空字符 (\\0)"); // 2. 使用 into_raw() 消耗 CString,返回裸指针 *mut c_char // 现在 C 代码负责管理这个指针指向的内存 c_string.into_raw() } // 这是一个配套的函数,用于安全地释放 C 代码使用完的指针 #[no_mangle] pub extern "C" fn free_c_string(ptr: *mut c_char) { // 必须在 unsafe 块中操作裸指针 if !ptr.is_null() { unsafe { // 使用 CString::from_raw() 重新获取 CString 的所有权。 // 当 from_raw() 创建的临时 CString 在作用域结束时, // 它的 Drop Trait 会自动运行,安全地释放内存。 let _ = CString::from_raw(ptr); } } }*/ fn main() { let rust_string = String::from("Hello from Rust FFI!"); // 示例如何调用: let c_ptr = rust_string_to_c(rust_string); println!("获取到 C 指针: {:?}", c_ptr); // ... 在这里,C 语言代码可以使用 c_ptr ... // 当 C 语言使用完毕后,调用释放函数 unsafe { rs_cstring_free(c_ptr);} }
这段代码展示了 Rust 中进行**外部函数接口(FFI,Foreign Function Interface)**操作的典型模式,用于安全地在 Rust 内存管理和 C 语言内存管理之间进行桥接。
该模式的核心在于:Rust 负责分配和释放内存,而 C 语言负责使用内存。
以下是代码的详细分析:
概览:FFI 内存管理模式
这段代码定义了两个配套的函数:
rust_string_to_c: 将 Rust 的 String 转换为 C 兼容的裸指针 (*mut c_char),并将所有权转移给 C。
rs_cstring_free: 接收 C 使用完的裸指针,将其所有权返还给 Rust,并安全释放内存。
函数分析:
rust_string_to_c/// Convert a String to C-compatible string
/// ... (Doc comments omitted for brevity) ...
pub fn rust_string_to_c(s: String) -> *mut c_char {
CString::new(s)
.map(|c_str| c_str.into_raw())
.unwrap_or(std::ptr::null_mut())
}
1. 输入与输出
s: String: 函数获取 RustString的所有权。这意味着原始的 Rust 变量在调用此函数后不能再使用。-> *mut c_char: 函数返回一个可变的 C 风格字符指针。在 C 语言中,这就是char*。
2. 核心逻辑与错误处理
CString::new(s): 这是关键的转换步骤。它尝试将 Rust 的 UTF-8String转换为 C 语言要求的、以空字符(\0)结尾的CString智能指针。- 这个方法返回一个
Result,因为如果原始String内部包含\0字节(不只是末尾),它会失败。
- 这个方法返回一个
.map(|c_str| c_str.into_raw()): 如果CString::new成功(Ok变体),则执行此映射。c_str.into_raw()是一个重要的 FFI 方法:它消耗CString的所有权,并返回一个裸指针*mut c_char。此时,Rust 的所有权系统停止跟踪这块内存。
.unwrap_or(std::ptr::null_mut()): 这是错误处理的核心。- 如果
.new()失败了(Err变体),它不会导致 Rust 程序崩溃(panic)。相反,它返回std::ptr::null_mut()(C 语言的NULL指针)。 - 优点: 这是 FFI 的最佳实践,允许 C 语言调用者通过检查是否返回
NULL来安全地处理错误。
- 如果
函数分析:
rs_cstring_free/// Free a CString allocated by Rust (for ex. using `rust_string_to_c`)
/// ... (Safety comments omitted for brevity) ...
#[no_mangle]
pub unsafe extern "C" fn rs_cstring_free(s: *mut c_char) {
if s.is_null() {
return;
}
drop(CString::from_raw(s));
}
1. FFI 接口定义
#[no_mangle]: 告诉 Rust 编译器不要“混淆”此函数的名称,确保 C 链接器能找到名为rs_cstring_free的符号。pub unsafe extern "C" fn: 标记此函数使用 C 语言的调用约定(ABI),并且要求调用者(C 语言代码)位于一个unsafe上下文,因为他们正在手动管理指针。
2. 核心逻辑与内存释放
if s.is_null() { return; }: 基本的安全性检查,防止对空指针进行操作。drop(CString::from_raw(s));: 这是内存释放的关键步骤。CString::from_raw(s)接收原始裸指针的所有权,并重新构建一个临时的CString智能指针。drop(...)确保这个临时的CString立即离开作用域。- 当
CString离开作用域时,它的DropTrait 会自动运行,使用 Rust 的内存分配器安全地释放指向的堆内存。
总结:安全性与所有权转移
这段代码完美地演示了 Rust FFI 的核心原则:
- 所有权转移:
into_raw()将所有权从 Rust 转移到 C 边界之外。 - 责任明确: C 语言代码负责使用指针,并在完成后调用
rs_cstring_free。 - 安全回收:
from_raw()将所有权从 C 边界带回 Rust,允许 Rust 的安全机制(DropTrait)自动清理内存。
浙公网安备 33010602011771号