如何将C字符串转换为Rust字符串并通过FFI返回?
我正在尝试获取C库返回的C字符串,并通过FFI将其转换为Rust字符串.
mylib.c const char* hello(){ return "Hello World!"; } main.rs #![feature(link_args)] extern crate libc; use libc::c_char; #[link_args = "-L . -I . -lmylib"] extern { fn hello() -> *c_char; } fn main() { //how do I get a str representation of hello() here? } 解决方法
在Rust中使用C字符串的最佳方法是使用
std::ffi 模块中的结构,即
CStr 和
CString .
CStr是动态大小的类型,因此它只能通过指针使用.这使它非常类似于常规str类型.您可以使用不安全的 您可以使用其 这是一个例子: extern crate libc; use libc::c_char; use std::ffi::CStr; use std::str; extern { fn hello() -> *const c_char; } fn main() { let c_buf: *const c_char = unsafe { hello() }; let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) }; let str_slice: &str = c_str.to_str().unwrap(); let str_buf: String = str_slice.to_owned(); // if necessary } 您需要考虑* const c_char指针的生命周期以及谁拥有它们.根据C API,您可能需要在字符串上调用特殊的释放函数.您需要仔细安排转换,以使切片不会超过指针. CStr :: from_ptr返回具有任意生命周期的& CStr的事实在这里有帮助(尽管它本身很危险);例如,您可以将C字符串封装到结构中并提供Deref转换,以便您可以像使用字符串切片一样使用结构: extern crate libc; use libc::c_char; use std::ops::Deref; use std::ffi::CStr; extern "C" { fn hello() -> *const c_char; fn goodbye(s: *const c_char); } struct Greeting { message: *const c_char,} impl Drop for Greeting { fn drop(&mut self) { unsafe { goodbye(self.message); } } } impl Greeting { fn new() -> Greeting { Greeting { message: unsafe { hello() } } } } impl Deref for Greeting { type Target = str; fn deref<'a>(&'a self) -> &'a str { let c_str = unsafe { CStr::from_ptr(self.message) }; c_str.to_str().unwrap() } } 此模块中还有另一种类型,称为 不幸的是,C字符串总是以零字节结束,并且不能在其中包含一个字符串,而Rust& [u8] / Vec< u8>完全相反 – 它们不以零字节结尾,并且可以在其中包含任意数量的它们.这意味着从Vec< u8>开始.到CString既没有错误也没有无分配 – CString构造函数都检查你提供的数据中的零,如果找到一些则返回错误,并在字节向量的末尾附加一个零字节,这可能需要重新分配. 与实现Deref< Target = str>的String类似,CString实现Deref< Target = CStr>,因此您可以直接在CString上调用CStr上定义的方法.这很重要,因为在CStr上定义了返回C互操作所需的* const c_char的 可以从可以转换为Vec< u8>的所有内容创建CString. String,& str,Vec< u8>和& [u8]是构造函数 extern crate libc; use libc::c_char; use std::ffi::CString; fn main() { let c_str_1 = CString::new("hello").unwrap(); // from a &str,creates a new allocation let c_str_2 = CString::new(b"world" as &[u8]).unwrap(); // from a &[u8],creates a new allocation let data: Vec<u8> = b"12345678".to_vec(); // from a Vec<u8>,consumes it let c_str_3 = CString::new(data).unwrap(); // and now you can obtain a pointer to a valid zero-terminated string // make sure you don't use it after c_str_2 is dropped let c_ptr: *const c_char = c_str_2.as_ptr(); // the following will print an error message because the source data // contains zero bytes let data: Vec<u8> = vec![1,2,3,4,5,6]; match CString::new(data) { Ok(c_str_4) => println!("Got a C string: {:p}",c_str_4.as_ptr()),Err(e) => println!("Error getting a C string: {}",e),} } 如果你需要将CString的所有权转移到C代码,你可以调用 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |