[英]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字符串,並傳入給定的字符串
#[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?")
我究竟做錯了什么?
您錯過了關鍵的一步。
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() })
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.