如何转换' struct'到'& [u8]'?

时间:2015-01-24 15:43:47

标签: rust

我想通过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,因为我想尽可能快地发送数据,所以我想要没有或只是很小的开销。

2 个答案:

答案 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);
}

然后,您就可以在任何地方发送字节。我假设您已经意识到天真地发送字节的问题(如潜在的字节序问题或版本控制),但我会在以下情况中提及它们^ _ ^。