1

我对 Rust 相当陌生,并且在我的项目中使用 bincode。枚举变体的编码是我在与现有服务器交互时一直试图处理的一个问题。旧项目是用 C 语言编写的,并定义了“fshort-enums”的编译器选项。

在旧的 C 项目中,我会定义一个类似的枚举:

enum test_enum {
    Start,
    Init,
    Complete,
}

然后当这个数据被放入数据包时,它将占用 1 个字节,如下所示:

0x00,
0x01,
0x02

如果我将枚举定义为:

enum test_enum {
    Start,
    Init,
    Complete = 65535,
}

然后当这个数据被放入数据包时,每个变体都会占用 2 个字节,如下所示:

0x00 0x00,
0x01 0x00,
0x02 0x00

现在我找到了一种方法来配置bincode枚举编码以从默认的 4 个字节更改为适合以下值的大小:

#[derive(Serialize, Deserialize)]
enum TestEnum {
    Start,
    Init,
    Complete = 65535,
}

let x = TestEnum::Complete;
if let Ok(bytes) = bincode::options().with_varint_encoding().serialize(&x) {
    println!("{:?}", bytes); // [2]
}

问题1:如何强制bincode使所有变体占用2个字节?

问题2:如何强制bincode应用枚举变量值?(在此示例TestEnum::Complete中,值为 2 而不是 65535)。

4

2 回答 2

1

Options::with_varint_encoding可能不是您想要的,因为它只会对小于 256 的整数使用 1 个字节。您可以使用Options::with_fixint_encoding与 Rust 类型相同的精度来编码整数,但您不需要指定它,因为它是已经默认了。

Serde 是结构性的;它并不真正关心数据在内存中的实际表示方式。您分配给枚举的数值会丢失,并且bincode只会看到变体的名称。

您可以通过配置serde在将枚举暴露给bincode序列化程序之前将其转换为整数来解决此问题:

#[derive(Serialize, Deserialize, Copy, Clone)]
#[serde(into = "u16")]
enum TestEnum {
    Start,
    Init,
    Complete = 65535,
}

impl From<TestEnum> for u16 {
    fn from(value: TestEnum) -> u16 {
        value as u16
    }
}

然后你的代码给出了预期的结果:

let x = TestEnum::Complete;
if let Ok(bytes) = bincode::serialize(&x) {
    println!("{:?}", bytes); // [255, 255]
}
于 2020-10-24T10:48:13.070 回答
0

我确实使用了@Peter Hall 提供的答案,但我必须为所有定义的枚举(有很多)添加一个实现,以便能够序列化它们,并且第二个实现能够反序列化。所以我继续我的调查并找到了一个对我来说似乎更有用的替代方案。我现在不是为每个枚举添加一个实现,而是使用 crates.io 中的num_enum crate 并向枚举添加一些属性。所以代码如下所示:

use num_enum::{IntoPrimitive, TryFromPrimitive};

#[derive(Serialize, Deserialize, Copy, Clone)]
#[serde(into = "u16", try_from = "u16")]
#[repr(u16)]
enum TestEnum {
    Start,
    Init,
    Complete = 65535,
}

我只是认为这可能会在将来对其他人有所帮助。

于 2020-11-19T05:29:52.473 回答