Rust 我可以将字节数组反序列化为结构吗?
我正在从套接字读取一系列字节,需要将n个字节的每一段作为一个项放入结构中Rust 我可以将字节数组反序列化为结构吗?,rust,Rust,我正在从套接字读取一系列字节,需要将n个字节的每一段作为一个项放入结构中 use std::mem; #[derive(Debug)] struct Things { x: u8, y: u16, } fn main() { let array = [22 as u8, 76 as u8, 34 as u8]; let foobar: Things; unsafe { foobar = mem::transmute::<[u8;
use std::mem;
#[derive(Debug)]
struct Things {
x: u8,
y: u16,
}
fn main() {
let array = [22 as u8, 76 as u8, 34 as u8];
let foobar: Things;
unsafe {
foobar = mem::transmute::<[u8; 3], Things>(array);
}
println!("{:?}", foobar);
}
使用std::mem;
#[导出(调试)]
构造事物{
x:u8,
y:u16,
}
fn main(){
让数组=[22作为u8,76作为u8,34作为u8];
让福巴:事情;
不安全{
foobar=mem::transmute::(数组);
}
println!(“{:?}”,foobar);
}
我得到的错误是,
foobar
是32位,而array
是24位。foobar
不应该是24位(8+16=24)?这里的问题是y
字段是16位对齐的。所以你的内存布局实际上是
x
padding
y
y
请注意,交换x
和y
的顺序没有任何帮助,因为Rust的结构内存布局实际上是未定义的(因此仍然是32位,原因很简单,只是在编译器中很简单)。如果你依赖它,你将得到未定义的行为
对齐的原因在中进行了解释
通过将属性repr(packed)
添加到结构中,可以防止对齐,但这样会失去性能和获取字段引用的能力:
#[repr(packed)]
struct Things {
x: u8,
y: u16,
}
最好的方法是根本不要使用
transmute
,而是手动提取值,并希望优化器能够快速执行:
let foobar = Things {
x: array[0],
y: ((array[1] as u16) << 8) | (array[2] as u16),
};
让foobar=Things{
x:数组[0],
y:((数组[1]作为u16)结构中的每个项之间是否总是有8位的填充?不,填充只是不相关的内容。填充的存在只是因为另一个字段是空的aligned@Fluffy不一定。如果x
也是u16
,则不需要填充。如果x
仍然是u8
,并且您有一个x2
字段t这也是u8
在x
和y
之间,您仍然不需要填充。您知道为什么吗?(注意,您可以查找结构对齐,而不考虑语言-原因总是一样的。)@Fluffy如果所有字段的大小都相同,则不会更快。逻辑是,u8
是8位对齐的,u16
是16位对齐的。这会在两者之间创建必要的8位空间。您可以选择自己使用该空间(通过将第一个字段扩展到16位或引入中间8位字段)或者您可以不使用它(称为填充),但不管您是否打算将其命名为字段,编译器都会将8位空间放在那里。(除非您明确要求使用压缩结构,如回答中所述。)@Fluffy,即使您以某种方式解决对齐问题(这不太可能),您仍然会遇到字节顺序的问题,如果您需要通过网络传递数据,这一点很重要。将字节数组重新解释为结构本身就不可移植。这就是为什么有很多序列化格式可用。请注意,这与许多其他答案的问题相同:它没有考虑对齐方式t、 目标类型中可能存在填充(对于u64
,这不是问题),或endianness。不需要在指针上使用transmute,只需强制转换指针。这将防止指针和指向指针的指针之间的意外转换,正如强制转换有些类型检查一样。正如其他人所说,这是不推荐的,但这正是OP所要求的,在技术上是正确的。endianness和其他细节o如果有人想走这条路,要小心。
use std::mem;
fn main() {
let bytes = vec!(0u8, 1u8,2u8, 3, 4, 5, 6, 7, 8, 9, 0xffu8, );
let data_ptr: *const u64 = unsafe { mem::transmute(bytes[0..4].as_ptr()) };
let data: u64 = unsafe { *data_ptr };
println!("{:#x}", data);
}