Rust str 和 ffi::CString 之间的转换并再次返回部分损坏了字符串 [英] Conversion between a Rust str and ffi::CString and back again partially corrupts the string
问题描述
#![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 字符串,传入给定的字符串
With this test that first converts to a CString
and then back again to a Rust string, passing in the given string
#[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 字符串结尾的往返行程,第一个字符位置为空:
will result in a round trip with a Rust string at the end with a null in the first character position:
Some("\u{0}狐狸说了什么?")
我做错了什么?
推荐答案
您错过了一个关键步骤.
You've missed one crucial step.
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
函数运行完成,该数据仍将被释放.这意味着以后的代码可以覆盖内存中的字符串内容.在您的示例中,恰好只有第一个字符被覆盖.
allocates a new CString
structure, and takes a pointer to its data, but that data will still be freed once the to_c_str
function runs to completion. This means that later code can overwrite the string contents in-memory. In your example case, it just happens to be that only the first character is overwritten.
我建议阅读的文档.as_ptr()
因为它试图涵盖其中的一些内容.
I'd recommend reading over the documentation of .as_ptr()
as it tries to cover some of this.
您可以手动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()
获取所有权并自行返回指针.
but the best approach would be to use .into_raw()
to take ownership and return the pointer on its own.
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() })
}
这篇关于Rust str 和 ffi::CString 之间的转换并再次返回部分损坏了字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!