我想通过TcpStream
发送我的结构。我可以发送String
或u8
,但我不能发送任意结构。例如:
struct MyStruct {
id: u8,
data: [u8; 1024],
}
let my_struct = MyStruct { id: 0, data: [1; 1024] };
let bytes: &[u8] = convert_struct(my_struct); // how??
tcp_stream.write(bytes);
收到数据后,我想将&[u8]
转换回MyStruct
。如何在这两种表示形式之间进行转换?
我知道Rust有一个用于序列化数据的JSON模块,但我不想使用JSON,因为我想尽可能快地发送数据,所以我想要没有或只是很小的开销。
答案 0 :(得分:18)
使用stdlib
和泛型函数可以完成正确大小的结构作为零复制字节。
在下面的示例中,有一个名为any_as_u8_slice
而不是convert_struct
的可重用函数,因为这是一个用于包装转换和切片创建的实用程序。
请注意,问题是关于转换,此示例创建一个只读片,因此具有不需要复制内存的优势。
以下是基于问题的实例:
unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
::std::slice::from_raw_parts(
(p as *const T) as *const u8,
::std::mem::size_of::<T>(),
)
}
fn main() {
struct MyStruct {
id: u8,
data: [u8; 1024],
}
let my_struct = MyStruct { id: 0, data: [1; 1024] };
let bytes: &[u8] = unsafe { any_as_u8_slice(&my_struct) };
// tcp_stream.write(bytes);
println!("{:?}", bytes);
}
注意1)即使第三方板条箱在某些情况下可能更好,但这是一个原始操作,它对于了解如何在Rust中有用。
注2)在撰写本文时(Rust 1.15),不支持const
函数。一旦有,就可以投射到固定大小的数组而不是切片。
注3) any_as_u8_slice
函数标记为unsafe
,因为struct
中的任何填充字节可能是未初始化的内存(给出未定义的行为)。
如果有办法确保输入参数仅使用#[repr(packed)]
的结构,那么它可能是安全的。
否则该函数是相当安全的,因为它可以防止缓冲区溢出,因为输出是只读的,固定的字节数,并且它的生命周期绑定到输入。
如果你想要一个版本返回一个&mut [u8]
,这将非常危险,因为修改可能很容易造成不一致/损坏的数据。
答案 1 :(得分:15)
(在类似问题上从Renato Zannon's comment无耻地被盗)
也许像bincode
这样的解决方案适合您的情况?这是一个有效的摘录:
<强> Cargo.toml 强>
[package]
name = "foo"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]
[dependencies]
serde_derive = "1.0.34"
bincode = "1.0.0"
serde = "1.0.34"
<强> main.rs 强>
extern crate bincode;
#[macro_use]
extern crate serde_derive;
extern crate serde;
use std::fs::File;
#[derive(Serialize, Deserialize)]
struct A {
id: i8,
key: i16,
name: String,
values: Vec<String>,
}
fn main() {
let a = A {
id: 42,
key: 1337,
name: "Hello world".to_string(),
values: vec!["alpha".to_string(), "beta".to_string()],
};
// Encode to something implementing Write
let mut f = File::create("/tmp/output.bin").unwrap();
bincode::serialize_into(&mut f, &a).unwrap();
// Or just to a buffer
let bytes = bincode::serialize(&a).unwrap();
println!("{:?}", bytes);
}
然后,您就可以在任何地方发送字节。我假设您已经意识到天真地发送字节的问题(如潜在的字节序问题或版本控制),但我会在以下情况中提及它们^ _ ^。