我正在将一些 C 函数绑定(bind)到 rust。我遇到了一个小问题,我想知道用 Rust 解决它的正确方法。
这是我想从 C API 调用的函数:
extern "C" {
pub fn H5Aread(attr_id: hid_t, type_id: hid_t, buf: *mut c_char) -> herr_t;
}
该函数从文件中读取内容,并将其存储在 buf
中.
所以,我在 vector 中创建了这个缓冲区:
let len: u64 = get_the_length();
let attr_raw_string: Vec<c_char> = Vec::new(); // c_char is equivalent to i8
attr_raw_string.resize(len as usize, 0);
let attr_raw_string_ptr = attr_raw_string.as_mut_ptr();
let read_error = H5Aread(attr_obj, attr_type, attr_raw_string_ptr);
if read_error < 0 {
panic!("...");
}
let result_str: String = String::from_utf8(attr_raw_string);
现在不能编译因为from_utf8
期望一个 Vec<u8>
,但是Vec<c_char>
是 Vec<i8>
.
有没有办法解决这个问题,而不必每次都将字符串复制并转换为新类型 u8
?
最佳答案
你快到了。
现在,我们假设您的 FFI 边界的 C 端是正确的 - 即它正确地生成了一个以 null 结尾的字符串。
为了在 Rust 中有效地分配和恢复它,我们将使用 CStr
.这会创建一个引用内存中 C 字符串的借用类型(即 *const char
)。这不会分配,因为它不是一个拥有的类型。
然后我们将其转换为 &str
与我们的预期进行最终比较。这仍然不是一个拥有的类型,所以我们创建的只是我们的 Vec<>
我们有效地用作缓冲区。
完整代码可在下方和 playground 上找到:
#[test]
fn test() {
let len:u64 = 64;
// Allocate a buffer
let mut buffer:Vec<c_char> = Vec::with_capacity(len as usize);
let attr_raw_string_ptr = buffer.as_mut_ptr();
let read_error = unsafe { H5Aread(attr_raw_string_ptr) };
if read_error < 0 {
panic!("...");
}
let result = unsafe {
CStr::from_ptr(attr_raw_string_ptr)
};
let result_str = result.to_str().unwrap();
assert_eq!(result_str, "test");
}
三个重要的陷阱:
-
CStr::to_str()
可能会失败(因此当Result<&str, _>
的内容不是有效的 utf-8 时它返回CStr
的原因。这是因为 rustString
和&str
类型都需要是有效的 utf-8。< - 显然,您的输入缓冲区需要至少有您的 C 函数将投入其中的大小。向C方提出保证即可。
-
CStr::from_ptr
有一个bunch of gotchas在使用它时你至少应该牢记这一点
关于c - Read raw C string to Rust...在此上下文中将有符号转换为无符号的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58146208/