如何将 'struct' 转换为 '&[u8]'?

How to convert 'struct' to '&[u8]'?

我想通过 TcpStream 发送我的结构。我可以发送 Stringu8,但我不能发送任意结构。例如:

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