You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中如何通用地将u32值反序列化为枚举Vec?

问题:将JSON的u32 Flags字段反序列化为Rust的Option<Vec>

需求

需要将JSON中u32类型的Flags字段,反序列化为Rust结构体中的Option<Vec<FooFlag>>类型字段。例如JSON中Flags:20需转换为vec![FooFlag::Example1, FooFlag::Example2](因为5+15=20),同时要实现通用逻辑,可复用在多个类似结构体上。

示例与定义

JSON输入示例

{
  "Account": "r...",
  "Flags": 20,
  ...
}

Rust目标结构体

#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all(serialize = "PascalCase", deserialize = "snake_case"))]
pub struct Foo<'a> {
  account: &'a str,
  flags: Option<Vec<FooFlag>>
}

FooFlag枚举定义

#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Display, AsRefStr)]
pub enum FooFlag {
  Example1 = 5,
  Example2 = 15,
  ...
}

现有待完善代码

当前已编写的转换模块:

mod flag_conversion {
    use alloc::vec::Vec;
    use serde::{Deserializer, Serializer, Deserialize};

    fn serialize<'a, T, S>(flags: &'a Option<Vec<T>>, s: S) -> Result<S::Ok, S::Error>
    where
        T: Into<u32>,
        S: Serializer,
    {
        s.serialize_option(
            {
                if let Some(flags) = &flags {
                    let transaction_flags: Vec<u32> = flags.into_iter().map(|&flag| {
                        let f = flag;
                        let n = f.into();
                        n
                    }).collect();
                    transaction_flags.iter().sum::<u32>()
                } else {
                    0
                }
            }
        )
    }

    fn deserialize<'de, D>(d: D) -> Result<D::Ok, D::Error>
    where
        D: Deserializer<'de>,
    {
        let specified_flags = u32::deserialize(d).unwrap();
        d.deserialize_u32(
            {
                if specified_flags == 0 {
                    None
                } else {
                    todo!()  // convert to `Vec` of `...Flag` enum
                }
            }
        )
    }
}

结构体中使用方式:

#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all(serialize = "PascalCase", deserialize = "snake_case"))]
pub struct Foo<'a> {
  account: &'a str,
  #[serde(with = "flag_conversion")]
  flags: Option<Vec<FooFlag>>
}

完善后的通用实现

1. 定义通用Trait

首先定义一个Trait,让所有Flag枚举实现该Trait以提供通用操作:

pub trait FlagU32 {
    // 返回枚举的所有变体
    fn all_variants() -> Vec<Self> where Self: Sized;
    // 返回变体对应的u32数值
    fn value(&self) -> u32;
}

2. 为FooFlag实现Trait

impl FlagU32 for FooFlag {
    fn all_variants() -> Vec<Self> {
        vec![FooFlag::Example1, FooFlag::Example2] // 列出所有枚举变体
    }

    fn value(&self) -> u32 {
        *self as u32
    }
}

3. 完善flag_conversion模块

mod flag_conversion {
    use alloc::vec::Vec;
    use serde::{Deserializer, Serializer, de::Error};
    use super::FlagU32;

    // 序列化逻辑:将Option<Vec<T>>转换为u32求和
    pub fn serialize<'a, T, S>(flags: &'a Option<Vec<T>>, s: S) -> Result<S::Ok, S::Error>
    where
        T: FlagU32,
        S: Serializer,
    {
        let sum = flags.as_ref()
            .map(|flags_list| flags_list.iter().map(|flag| flag.value()).sum::<u32>())
            .unwrap_or(0);
        s.serialize_u32(sum)
    }

    // 反序列化逻辑:将u32拆分为对应的枚举变体列表
    pub fn deserialize<'de, T, D>(d: D) -> Result<Option<Vec<T>>, D::Error>
    where
        T: FlagU32,
        D: Deserializer<'de>,
    {
        let specified_flags = u32::deserialize(d)?;

        // 0值对应None
        if specified_flags == 0 {
            return Ok(None);
        }

        let mut remaining = specified_flags;
        let mut result = Vec::new();

        // 遍历所有枚举变体,匹配数值
        for variant in T::all_variants() {
            let val = variant.value();
            if val != 0 && remaining >= val {
                remaining -= val;
                result.push(variant);
            }
        }

        // 若剩余值不为0,说明存在无法识别的flags,返回错误
        if remaining != 0 {
            return Err(D::Error::custom(format!("无法识别的flags值: {}", remaining)));
        }

        Ok(Some(result))
    }
}

4. 适配按位掩码场景(可选)

如果你的flags是按位掩码(每个枚举变体对应一个独立比特位,例如Example1=4(0b100)、Example2=16(0b10000),按位或得20),可修改反序列化中的匹配逻辑:

// 替换deserialize中的循环部分
for variant in T::all_variants() {
    let val = variant.value();
    if val != 0 && (specified_flags & val) != 0 {
        result.push(variant);
    }
}

// 检查是否存在未知比特位
let all_known_flags: u32 = T::all_variants().iter().map(|f| f.value()).sum();
if (specified_flags & !all_known_flags) != 0 {
    return Err(D::Error::custom(format!("存在未知flags比特位: {}", specified_flags & !all_known_flags)));
}

使用说明

所有需要转换的Flag枚举只需实现FlagU32 trait,在结构体字段上添加#[serde(with = "flag_conversion")]即可复用该通用逻辑。

内容的提问来源于stack exchange,提问作者LimpidCrypto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 05:40:22