如何使用 Bincode 在 Rust 中序列化 Enum,同时保留 Enum 判别式而不是索引?

2024-04-10

我一直在使用 bincode 在 Rust 中序列化枚举,但我面临一个问题,我收到枚举变体的索引而不是其指定的判别式。这是我尝试序列化的枚举的示例:

    #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
    #[repr(u64)]
    pub enum StreamOutputFormat {
        A(X) = 0x01,
        B(Y) = 0x02,
        C(Z) = 0x03,
    }

在此代码中,X、Y 和 Z 代表结构体。

当我尝试序列化一个实例时StreamOutputFormat::C(Z {some_z_stuff...})像这样使用 bincode:

let sof = StreamOutputFormat::C(Z {some_z_stuff...});
println!("sof: {:?}", bincode::serialize(&sof));

我得到的输出是:

[2, 0, 0, 0, 0, 0, 0, 0, ...]

这是有问题的,因为由于与其他组件的互操作性要求,我需要序列化输出作为枚举变量(在本例中为 0x03)的判别式,而不是索引。为了进行比较,我的代码库中作为单元枚举的其他枚举使用正确序列化(De)Serialize_repr.

在二进制代码中序列化(和反序列化)这种类型的枚举以便我收到枚举变体判别式而不是其索引的正确方法是什么?


让事情变得复杂的是 serde 的枚举反序列化只允许字符串、字节或u32对于枚举标记(每种格式选择这三种格式之一)。例如,这被硬编码到每种格式中这里是二进制代码 https://docs.rs/bincode/latest/src/bincode/de/mod.rs.html#284。带有一个枚举u64就 serde 而言,tag 本质上不是枚举。

因此,考虑到这一点,您必须将枚举序列化和反序列化为枚举以外的其他内容。我选择使用元组,它可能与您所得到的最接近的枚举。序列化非常简单,因为我们知道所有内容是什么类型。

impl Serialize for StreamOutputFormat {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            StreamOutputFormat::A(x) => {
                let mut tuple = serializer.serialize_tuple(2)?;
                tuple.serialize_element(&0x01u64)?;
                tuple.serialize_element(x)?;
                tuple.end()
            }
            StreamOutputFormat::B(y) => {
                let mut tuple = serializer.serialize_tuple(2)?;
                tuple.serialize_element(&0x02u64)?;
                tuple.serialize_element(y)?;
                tuple.end()
            }
            StreamOutputFormat::C(z) => {
                let mut tuple = serializer.serialize_tuple(2)?;
                tuple.serialize_element(&0x03u64)?;
                tuple.serialize_element(z)?;
                tuple.end()
            }
        }
    }
}

为了清晰起见,我一直重复这一点。如果您的变体具有多个字段,则需要增加传递给的数字serialize_tuple。也不要忘记判别式需要u64.

现在进行反序列化。

impl<'de> Deserialize<'de> for StreamOutputFormat {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::de::Error;

        struct StreamOutputFormatVisitor;

        impl<'de> Visitor<'de> for StreamOutputFormatVisitor {
            type Value = StreamOutputFormat;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(
                    formatter,
                    "a tuple of size 2 consisting of a u64 discriminant and a value"
                )
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: serde::de::SeqAccess<'de>,
            {
                let discriminant: u64 = seq
                    .next_element()?
                    .ok_or_else(|| A::Error::invalid_length(0, &self))?;
                match discriminant {
                    0x01 => {
                        let x = seq
                            .next_element()?
                            .ok_or_else(|| A::Error::invalid_length(1, &self))?;
                        Ok(StreamOutputFormat::A(x))
                    }
                    0x02 => {
                        let y = seq
                            .next_element()?
                            .ok_or_else(|| A::Error::invalid_length(1, &self))?;
                        Ok(StreamOutputFormat::B(y))
                    }
                    0x03 => {
                        let z = seq
                            .next_element()?
                            .ok_or_else(|| A::Error::invalid_length(1, &self))?;
                        Ok(StreamOutputFormat::C(z))
                    }
                    d => Err(A::Error::invalid_value(
                        serde::de::Unexpected::Unsigned(d),
                        &"0x01, 0x02, or 0x03",
                    )),
                }
            }
        }

        deserializer.deserialize_tuple(2, StreamOutputFormatVisitor)
    }
}

通过样板,我们有一个电话deserialize_tuple,这将调用visit_seq在访客身上。在该方法中,访问者消耗了u64用作判别式,然后根据该判别式使用内部数据。

不起作用的方法

您无法通过向枚举添加虚拟字段来序列化它,因为这仍然会使用u32标签。您可以尝试的另一件事是通过反序列化(u64, UntaggedEnum) where UntaggedEnum is:

#[derive(Deserialize)]
#[serde(untagged)]
UntaggedEnum {
    A(X),
    B(Y),
    C(Z),
}

这不起作用,因为非自描述格式无法处理未标记的枚举。最重要的是,如果数据对多个变体有效,即使是自描述格式也可能会失败,因为没有简单的方法可以根据第一个元素有条件地反序列化元组的第二个元素。它的效率也很低,因为它会尝试反序列化枚举,即使u64无效。

Notes

您可能已添加#[repr(u64)]不知道 serde 枚举需要u32,你实际上可以使用#[repr(u32)]枚举也是如此。如果是这样的话,看来你可以使用 serde 的 enum 反序列化,它会稍微简单一些(与Deserialize宏生成)。据我所知,您需要将判别式映射到它们各自的变体。

值得注意的是,我编写的代码从未引用枚举定义中给出的实际判别式。这对于 Rust 来说是相当标准的,因为枚举判别式的功能非常少。你需要做指针转换 https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting甚至阅读它们,它们在有条件反序列化中绝对没有用处X, Y, and Z。如果删除它们,不会对序列化产生影响。

如果您打算修改此枚举,或者它有大量变体,那么最好利用时间将其转换为宏。作为声明性宏,这不会太难,因为您需要的所有值都是像这样的文字0x01并且重复是显而易见的。

我还没有检查过 bincode 2.0,其中包括它自己的Decode不使用 serde 的特征。或许可以解码u64enum标签,但结构与serde完全不同,所以我没有过多研究它。

这可能与您想要的格式不匹配。判断通过u64repr,您尝试反序列化的任何内容都不是由 serde 枚举生成的,因此我不可能知道您的格式是否与我使用的元组格式匹配。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Bincode 在 Rust 中序列化 Enum,同时保留 Enum 判别式而不是索引? 的相关文章

随机推荐

  • Firebase - 从数据库节点获取随机子节点

    我正在写一个Firebase Cloud Function这是从一个调用的Android应用程序 该函数应该从用户节点中获取一个随机用户Database 任何随机用户 但不是发送请求的用户 并将其返回给客户端 问题在于数据不是以数组的形式构
  • 如何正确地将日期转换为字符串并以 symfony 形式转换回来,以便可以使用日期选择器

    在我的 Symfony 4 应用程序中 我有一个包含日期类型字段的实体 默认情况下 Symfony 将其呈现为一组月 日 年的选择框 我已将其更改为文本小部件 以便我可以使用 jQuery UI 日期选择器 但我在尝试提交表格时遇到了问题
  • SCOM 上的 PowerShell 无法导入模块

    我有一个问题 如果没有帮助 我无法解决 我在第一台 PC 上有 SCOM 在第二台 PC 上有 SCOM 代理 当我的类在代理 PC 中发现时 它必须运行 PowerShell 脚本 该脚本包含命令 导入模块故障转移集群 但此命令失败并出现
  • 是什么导致 Web 服务 URL 和命名空间之间存在差异?

    我有一个包含 Web 服务的 ASP NET Web 项目 当我运行该服务时 它会将我带到一个显示所有公开方法的页面 使用类似于以下内容的 URLhttp api example com game service asmx 在 Web 服务
  • MySql - 大小 VARCHAR

    很多人对我说设置 VARCHAR 100 没有意义 它与 put 255 相同 我想知道为什么 那是垃圾 他们可能正在谈论这样一个事实 varchar无论最大长度是 100 还是 255 超过该长度的长度将使用两个字节 最多 64K 都使用
  • 关于 NSRunLoop,需要澄清

    When i Logger logger Logger new NSURL url NSURL URLWithString http www google com NSURLRequest request NSURLRequest requ
  • 如何使用 Linq To XML 获取元素值

    使用 Linq To XML 如何从下面的 xml 中获取 space id 值 720 我在读this http msdn microsoft com en us library bb308960 aspx但我认为xml中的命名空间是我的
  • 让 GitLab CI 克隆私有存储库

    我设置了 GitLab 和 GitLab CI 来托管和测试我的一些私人存储库 对于此系统下的作曲家模块 我设置了 Satis 来解析我的私有包 显然这些私有包需要 ssh 密钥来克隆它们 并且我在终端中可以使用它 我可以运行compose
  • ADO.Net (Azure AD) 错误“不支持关键字:身份验证”

    我正在尝试通过 C 代码使用 Azure AD 凭据连接到 Azure 数据库 代码如下 它在我的系统上运行良好 但是当我将它部署到32位VM时 它显示错误 不支持关键字 身份验证 VM 安装了 Net Framework 4 5 但未安装
  • 仅 Detox 测试启动画面

    我正在我的 React Native 项目上运行 detox 并且只能测试启动屏幕 启动屏幕进入登录屏幕 但排毒代码不允许我测试此元素 测试代码 describe Splash gt beforeEach async gt await de
  • java - 如何检查我的对象是否属于给定类的类型?

    我的方法获取 Class 作为参数 我必须检查我的变量是否属于 class 类型 Volvo v1 new Volvo Class aClass v1 getClass check aClass 在里面我需要做类似的事情 v2 instan
  • 输出到另一个窗口

    有没有办法直接在 VB NET 中打开窗体并在第二个监视器中最大化 也就是说 如果显示两个监视器 默认情况下第二个窗口中的表单加载会最大化吗 假设一个程序是用两种形式编写的 并且一台计算机连接了两个显示器 我希望 FormA 默认显示在 M
  • PyCharm:无法使用 docker-compose 创建远程 python 解释器

    尝试在 PyCharm 中创建 docker compose python 解释器时出现错误 解析 Users belek Projects project docker compose yml 时出错 进程docker compose c
  • 按一列中的因素拆分/子集数据框[重复]

    这个问题在这里已经有答案了 我的数据是这样的 例如 ID Rate State 1 24 AL 2 35 MN 3 46 FL 4 34 AL 5 78 MN 6 99 FL Data structure list ID 1 6 Rate
  • 致命错误:AST 文件格式错误或损坏 - Xcode

    在最新版本的 Xcode 中构建我的应用程序时出现此错误 致命错误 AST 文件格式错误或损坏 无法加载模块 Users me Library Developer Xcode DerivedData ModuleCache XYZYIE6Z
  • CMake FindThreads.cmake 找不到我的 pthreads.h 标头

    我在用着android cmake http code google com p android cmake 编译 Android 应用程序 这实质上创建了一个 CMake 工具链文件 用于使用 Android NDK 提供的工具链 As
  • Markdown 中的 RTL

    是否有任何现有的 Markdown 插件规范包含对 RTL 语言的支持 我希望是这样的 This paragraph is left to right lt This paragraph is right to left 或者其他什么 我可
  • Jquery Sortable,通过拖出删除当前Item

    我的问题 sortable 事件 out 当我在列表中拖动某些内容或对列表进行排序时触发 但我只想在拖出项目时启动该功能 My code document ready function ust div1 sortable out funct
  • 正则表达式最多匹配 4 个空格

    我有一个正则表达式来匹配一个人的名字 到目前为止 我有 a zA Z s 但我想添加一个检查以允许最多 4 个空格 我该如何修改它才能做到这一点 Edit 我的意思是字符串中任意位置有 4 个空格 不要尝试使用正则表达式验证名称 人们可以随
  • 如何使用 Bincode 在 Rust 中序列化 Enum,同时保留 Enum 判别式而不是索引?

    我一直在使用 bincode 在 Rust 中序列化枚举 但我面临一个问题 我收到枚举变体的索引而不是其指定的判别式 这是我尝试序列化的枚举的示例 derive Debug PartialEq Eq Serialize Deserializ