簡體   English   中英

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字符串,並傳入給定的字符串

#[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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM