有没有一个安全的CString::new()替代方案,它不需要打开包装



我在一个FFI模块中有一些代码,它将Rust字符串转换为要发送回调用方的CStrings。

我发现CString::new()的调用约定很难使用。具体而言:

如果提供的字节包含内部0字节,此函数将返回错误。返回的NulError将包含字节以及nul字节的位置。

基本上,如果你不想使用unwrap()并想安全地调用这个函数,你就必须执行以下操作:

let cstr = match CString::new(message) {
Ok(cstr) => { cstr }
Err(_) => { CString::new("error converting string!").unwrap() }
};

从理论上讲,这可能会更好,因为Err值包含空字节的索引,我可以在这一点上截断字符串,但这会有点麻烦。

为什么?因为对CString::new(message)的第一次调用会消耗字符串,所以我必须在进行调用之前克隆字符串,这样我就可以在匹配的Err臂中再次使用它,这是一个无论如何都不应该被调用的代码路径。

我之前考虑过扫描null,但我对Unicode的了解有限,我不愿意只从Unicode字符串中删除null字节。

基本上我的问题是,有更好的方法吗?我希望CString::new(message)只在第一个null处截断字符串。它可以在Err值中返回截断的CString

也许还有另一个更容易使用的电话。我是不是错过了什么?

(编辑:顺便说一句,我只是将ASCII字符串传递给这个调用。(

编辑:我根据@kmdreko:接受的答案选择了这个解决方案

pub unsafe fn log_to_c_callback(level: LogLevel, mut message: String)  {
if let Some(callback) = LOGGER_CALLBACK {
message.retain(|c| c != '');
let len = message.len();
#[allow(clippy::unwrap_used)]
// Unwrap is safe because we removed all the null bytes above.
let message_cstr = CString::new(message).unwrap();

callback(level, message_cstr.as_ptr(), len as i32);
}
}

我之前曾考虑过扫描null,但我对Unicode的了解有限,我不愿意只从Unicode字符串中删除null字节。

我对Unicode的了解不那么有限。您可以安全地搜索和删除空字符。多字节字符不会有全零字节(维基百科关于UTF-8编码(,即使有,Rustchar也是Unicode标量值,而不是简单字节。

let mut message = String::from("hello wrld");
message.retain(|c| c != '');
let cstr = CString::new(message).expect("should not have null characters");

你可以利用这个机会过滤掉其他令人讨厌的字符,比如控制字符、换行符,或者你喜欢的任何字符。


如果你真的不想要.unwrap()/.expect(),你可以使用你的原始计划,但不需要克隆。CCD_ 10类型还返回原始的"0";字符串";通过.into_vec():

let message = String::from("hello wrld");
let mut message = message.into_bytes();
let cstr = loop {
match CString::new(message) {
Ok(cstr) => break cstr,
Err(err) => {
let idx = err.nul_position();
message = err.into_vec();
message.remove(idx);
}
}
};

相关内容

最新更新