我有一个未知大小的字节缓冲区,我想创建一个指向缓冲区开头内存的本地struct变量。按照我在C中所做的,我在Rust中尝试了很多不同的东西并且不断出错。这是我最近的尝试:
use std::mem::{transmute, size_of};
#[repr(C, packed)]
struct my_struct {
foo: u16,
bar: u8,
}
fn main() {
let v: Vec<u8> = vec![1, 2, 3];
let buffer = v.as_slice();
let s: my_struct = unsafe { transmute(buffer[..size_of::<my_struct>()]) };
}
我收到以下消息的错误:
std::marker::Sized
[u8]
[u8]
在编译时没有已知的常量std::mem::transmute
答案 0 :(得分:9)
您可以使用std::ptr::read
和std::ptr::write
直接读取/写入对象(std::ptr
值得检查的其他方法)。
在您的情况下,这很简单:
fn main() {
let v: Vec<u8> = vec![1, 2, 3];
let s: MyStruct = unsafe { std::ptr::read(v.as_ptr() as *const _) };
println!("here is the struct: {:?}", s);
}
我个人建议您在可重用的方法中将其包装起来,并在尝试读取之前对源缓冲区执行长度检查。
答案 1 :(得分:2)
如果您不想将数据复制到结构中,而是将其保留在原位,则可以使用slice::align_to
。而是创建一个&MyStruct
:
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
struct MyStruct {
foo: u16,
bar: u8,
}
fn main() {
let v = vec![1u8, 2, 3];
// I copied this code from Stack Overflow
// without understanding why this case is safe.
let (head, body, _tail) = unsafe { v.align_to::<MyStruct>() };
assert!(head.is_empty(), "Data was not aligned");
let my_struct = &body[0];
println!("{:?}", my_struct);
}
在这里,使用align_to
将某些字节转换为MyStruct
是安全的,因为我们使用了repr(C, packed)
并且MyStruct
中的所有类型都可以是任意字节
另请参阅:
答案 2 :(得分:0)
我放弃了转化的东西。 Rust中的*mut
(原始指针)非常类似于C指针,所以这很容易:
#[repr(C, packed)] // necessary
#[derive(Debug)] // not necessary
struct my_struct {
foo: u16,
bar: u8,
}
fn main() {
let v: Vec<u8> = vec![1, 2, 3];
let buffer = v.as_slice();
let mut s_safe: Option<&my_struct> = None;
let c_buf = buffer.as_ptr();
let s = c_buf as *mut my_struct;
unsafe {
// took these out of the unsafe block, as Peter suggested
// let c_buf = buffer.as_ptr();
// let s = c_buf as *mut my_struct;
let ref s2 = *s;
s_safe = Some(s2);
}
println!("here is the struct: {:?}", s_safe.unwrap());
}
当然,unsafe
标签没有笑话。但是我使用它的方式,我知道我的缓冲区已经填满,并在以后采取涉及字节序的适当预防措施。