如何将 'struct' 转换为 '&[u8]'?
How to convert 'struct' to '&[u8]'?
我想通过 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 因为我想尽可能快地发送数据,所以我想没有或非常小的开销。
(在类似问题上无耻地盗用并改编自 Renato Zannon's comment)
也许像 bincode
这样的解决方案适合您的情况?这是工作摘录:
Cargo.toml
[package]
name = "foo"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"
[dependencies]
bincode = "1.0"
serde = { version = "1.0", features = ["derive"] }
main.rs
use serde::{Deserialize, Serialize};
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);
}
然后您就可以将字节发送到任何您想要的地方。我假设您已经意识到天真地发送字节的问题(例如潜在的字节顺序问题或版本控制),但我会提及它们以防万一^_^。
zero-copied 字节的正确大小的结构可以使用 stdlib
和通用函数来完成。
在下面的示例中,有一个名为 any_as_u8_slice
而不是 convert_struct
的可重用函数,因为这是一个用于包装转换和切片创建的实用程序。
注意问题问的是converting,这个例子创建了一个read-only slice,所以优点是不需要复制内存。
这是一个基于问题的工作示例:
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) 尽管 3rd 方板条箱在某些情况下可能更好,但这是一个如此原始的操作,知道如何在 Rust 中进行操作很有用。
注 2) 在撰写本文时 (Rust 1.15),不支持 const
函数。一旦有了,就可以转换为固定大小的数组而不是切片。
注 3) any_as_u8_slice
函数被标记为 unsafe
因为 struct
中的任何填充字节可能是未初始化的内存(给出 undefined行为)。
如果有一种方法可以确保输入参数仅使用 #[repr(packed)]
的结构,那么它可能是安全的。
否则该函数相当安全,因为它阻止缓冲区 over-run,因为输出是 read-only,固定字节数,并且其生命周期与输入绑定。
如果您想要一个返回 &mut [u8]
的版本,这将非常危险,因为修改很容易创建 inconsistent/corrupt 数据。
如果你想存储到数据库或文件使用(bincode)
如果你想通过网络发送到另一个程序
使用快速、安全、经过良好测试、令人惊叹的模式的 gRPC
我想通过 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 因为我想尽可能快地发送数据,所以我想没有或非常小的开销。
(在类似问题上无耻地盗用并改编自 Renato Zannon's comment)
也许像 bincode
这样的解决方案适合您的情况?这是工作摘录:
Cargo.toml
[package]
name = "foo"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"
[dependencies]
bincode = "1.0"
serde = { version = "1.0", features = ["derive"] }
main.rs
use serde::{Deserialize, Serialize};
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);
}
然后您就可以将字节发送到任何您想要的地方。我假设您已经意识到天真地发送字节的问题(例如潜在的字节顺序问题或版本控制),但我会提及它们以防万一^_^。
zero-copied 字节的正确大小的结构可以使用 stdlib
和通用函数来完成。
在下面的示例中,有一个名为 any_as_u8_slice
而不是 convert_struct
的可重用函数,因为这是一个用于包装转换和切片创建的实用程序。
注意问题问的是converting,这个例子创建了一个read-only slice,所以优点是不需要复制内存。
这是一个基于问题的工作示例:
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) 尽管 3rd 方板条箱在某些情况下可能更好,但这是一个如此原始的操作,知道如何在 Rust 中进行操作很有用。
注 2) 在撰写本文时 (Rust 1.15),不支持 const
函数。一旦有了,就可以转换为固定大小的数组而不是切片。
注 3) any_as_u8_slice
函数被标记为 unsafe
因为 struct
中的任何填充字节可能是未初始化的内存(给出 undefined行为)。
如果有一种方法可以确保输入参数仅使用 #[repr(packed)]
的结构,那么它可能是安全的。
否则该函数相当安全,因为它阻止缓冲区 over-run,因为输出是 read-only,固定字节数,并且其生命周期与输入绑定。
如果您想要一个返回 &mut [u8]
的版本,这将非常危险,因为修改很容易创建 inconsistent/corrupt 数据。
如果你想存储到数据库或文件使用(bincode) 如果你想通过网络发送到另一个程序
使用快速、安全、经过良好测试、令人惊叹的模式的 gRPC