前言
最近在rust 调用c的时候遇到一个坑,需要从c里面把char *
转成rust的slice
类型。
最初使用CStr::from_ptr
函数,最后发现数据不对。
一段不正确的代码
下面的代码,在返回二进制数据,有时候不全
1
2
3
4
|
let slice = CStr::from_ptr(grammar_data as *const c_char)
.to_bytes()
.to_vec();
|
阅读from_ptr
实现
看了from_ptr
实现,焕然大悟,构造的len是通过sys::strlen
函数取得。
c里面的char *
是个裸指针,字符串的长度是通过,找到末尾的0
计算得到。
如果数据里面有0
,那返回的 rust slice
就是错误的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
// SAFETY: The caller has provided a pointer that points to a valid C
// string with a NUL terminator of size less than `isize::MAX`, whose
// content remain valid and doesn't change for the lifetime of the
// returned `CStr`.
//
// Thus computing the length is fine (a NUL byte exists), the call to
// from_raw_parts is safe because we know the length is at most `isize::MAX`, meaning
// the call to `from_bytes_with_nul_unchecked` is correct.
//
// The cast from c_char to u8 is ok because a c_char is always one byte.
unsafe {
let len = sys::strlen(ptr);
let ptr = ptr as *const u8;
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
}
}
|
正解代码
好了,找到原因,修复就简单。std::slice::form_raw_parts
可以传递裸指针
和长度
构造一个slice
。
这里有个小插曲,grammar_data是*mut ::std::os::raw::c_void;
类型,直接通过fs::write(file_name, slice)
写入slice
是不行的。所以这里把grammar_data 强转成 *const u8
类型。
1
2
3
4
|
unsafe {
let slice = std::slice::from_raw_parts(grammar_data as *const u8, size as usize);
}
|
github代码示例
https://github.com/guonaihong/rust-example/tree/main/raw-ptr-to-slice