#![allow(non_camel_case_types)]
use libc::{c_uchar, size_t};
use std::str::FromStr;
use std::ffi::{CString, NulError};
use std::slice;
#[repr(C)]
pub struct c_str_t {
pub len: size_t,
pub data: *const c_uchar,
}
pub trait MyCStrExt<T> {
fn to_c_str(&self) -> Result<c_str_t, NulError>;
}
pub trait MyCStringExt {
fn from_c_str_ref(nstr: &c_str_t) -> Option<String>;
}
impl<'a> MyCStrExt<&'a str> for str {
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.as_ptr() as *const u8, len: self.len() })
}
}
impl MyCStringExt for String {
fn from_c_str_ref(nstr: &c_str_t) -> Option<String> {
unsafe {
if nstr.data.is_null() {
return None;
}
let value = slice::from_raw_parts(nstr.data, nstr.len);
match String::from_utf8(value.to_vec()) {
Ok(value) => Some(value),
Err(e) => None
}
}
}
}
使用此测试首先转换为CString
,然后再返回到Rust字符串,传入给定的字符串
#[test]
fn test_to_c_str() {
let s = "What does the fox say?";
let result = s.to_c_str();
let round_trip = String::from_c_str_ref(result.as_ref().ok().unwrap());
println!("{:?}", round_trip);
}
将导致最后一个带有Rust字符串的往返,在第一个字符位置为null:
Some("\u{0}hat does the fox say?")
我做错了什么?
答案 0 :(得分:5)
你错过了一个关键步骤。
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.as_ptr() as *const u8, len: self.len() })
}
分配一个新的CString
结构,并获取指向其数据的指针,但是一旦to_c_str
函数运行完成,该数据仍将被释放。这意味着以后的代码可以覆盖内存中的字符串内容。在您的示例中,恰好只会覆盖第一个字符。
我建议阅读.as_ptr()
的文档,因为它试图涵盖其中一些内容。
您可以手动std::mem::forget
,例如
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
let s = c_str_t { data: result.as_ptr() as *const u8, len: self.len() };
std::mem::forget(result);
Ok(s)
}
但最好的方法是使用.into_raw()
取得所有权并自行返回指针。
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.into_raw() as *const u8, len: self.len() })
}