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: 函数获取 Rust String 的所有权。这意味着原始的 Rust 变量在调用此函数后不能再使用。
  • -> *mut c_char: 函数返回一个可变的 C 风格字符指针。在 C 语言中,这就是 char*
2. 核心逻辑与错误处理
  • CString::new(s): 这是关键的转换步骤。它尝试将 Rust 的 UTF-8 String 转换为 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 离开作用域时,它的 Drop Trait 会自动运行,使用 Rust 的内存分配器安全地释放指向的堆内存。
总结:安全性与所有权转移
这段代码完美地演示了 Rust FFI 的核心原则:
  • 所有权转移: into_raw() 将所有权从 Rust 转移到 C 边界之外。
  • 责任明确: C 语言代码负责使用指针,并在完成后调用 rs_cstring_free
  • 安全回收: from_raw() 将所有权从 C 边界带回 Rust,允许 Rust 的安全机制(Drop Trait)自动清理内存。
 
 
 
posted @ 2025-12-08 15:20  PKICA  阅读(6)  评论(0)    收藏  举报