Rust str 和 ffi::CString 之间的转换并再次返回部分损坏了字符串 [英] Conversion between a Rust str and ffi::CString and back again partially corrupts the string

查看:82
本文介绍了Rust str 和 ffi::CString 之间的转换并再次返回部分损坏了字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

#![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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆