Rust中如何实现嵌套枚举与字符串的相互转换?
Rust嵌套枚举与字符串双向转换的解决方案
方法一:基于Serde自定义序列化/反序列化
利用Serde的序列化框架,自定义逻辑生成和解析目标格式的字符串,无需手动维护大量match分支。
首先添加依赖:
[dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"
实现代码:
use serde::{Serialize, Deserialize, Serializer, Deserializer}; #[derive(Debug, Clone, PartialEq)] pub enum CharacterCreationAction { Open, } #[derive(Debug, Clone, PartialEq)] pub enum InteractionCode { MenuCharacterCreation { action: CharacterCreationAction }, } // 子枚举的序列化与反序列化 impl Serialize for CharacterCreationAction { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { let s = match self { CharacterCreationAction::Open => "Open", }; serializer.serialize_str(s) } } impl<'de> Deserialize<'de> for CharacterCreationAction { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; match s.as_str() { "Open" => Ok(CharacterCreationAction::Open), _ => Err(serde::de::Error::custom(format!("未知操作: {}", s))), } } } // 父枚举的自定义序列化,生成"前缀_子枚举值"格式 impl Serialize for InteractionCode { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { let s = match self { InteractionCode::MenuCharacterCreation { action } => { // 序列化子枚举后去掉引号,拼接成目标格式 format!("MenuCharacterCreation_{}", serde_json::to_string(action).unwrap().trim_matches('"')) } }; serializer.serialize_str(&s) } } // 父枚举的反序列化,拆分字符串后解析子枚举 impl<'de> Deserialize<'de> for InteractionCode { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; let parts: Vec<&str> = s.split('_').collect(); if parts.len() != 2 { return Err(serde::de::Error::custom(format!("无效格式: {}", s))); } match parts[0] { "MenuCharacterCreation" => { let action = serde_json::from_str(&format!("\"{}\"", parts[1])) .map_err(serde::de::Error::custom)?; Ok(InteractionCode::MenuCharacterCreation { action }) } _ => Err(serde::de::Error::custom(format!("未知交互类型: {}", parts[0]))), } } } // 测试示例 fn main() { let code = InteractionCode::MenuCharacterCreation { action: CharacterCreationAction::Open }; let serialized = serde_json::to_string(&code).unwrap(); println!("序列化结果: {}", serialized); // 输出 "MenuCharacterCreation_Open" let parsed: InteractionCode = serde_json::from_str(&serialized).unwrap(); assert_eq!(parsed, code); }
方法二:使用nested-enum-derive crate
该 crate 专为嵌套枚举设计,可自动生成字符串转换方法,省去手动编写代码的麻烦。
添加依赖:
[dependencies] nested-enum-derive = "0.1"
实现代码(将结构体变体改为元组变体以适配crate):
use nested_enum_derive::NestedEnum; #[derive(Debug, Clone, PartialEq, NestedEnum)] pub enum CharacterCreationAction { Open, } #[derive(Debug, Clone, PartialEq, NestedEnum)] pub enum InteractionCode { MenuCharacterCreation(CharacterCreationAction), } // 测试示例 fn main() { let code = InteractionCode::MenuCharacterCreation(CharacterCreationAction::Open); let serialized = code.as_str(); assert_eq!(serialized, "MenuCharacterCreation_Open"); let parsed = InteractionCode::from_str(serialized).unwrap(); assert_eq!(parsed, code); }
方法三:手动封装转换Trait(轻量方案)
如果不想引入额外依赖,可定义通用Trait并为每个枚举实现,逻辑清晰且适合小型项目。
pub trait EnumToString { fn to_string(&self) -> String; } pub trait StringToEnum: Sized { fn from_string(s: &str) -> Option<Self>; } impl EnumToString for CharacterCreationAction { fn to_string(&self) -> String { match self { CharacterCreationAction::Open => "Open".to_string(), } } } impl StringToEnum for CharacterCreationAction { fn from_string(s: &str) -> Option<Self> { match s { "Open" => Some(Self::Open), _ => None, } } } impl EnumToString for InteractionCode { fn to_string(&self) -> String { match self { InteractionCode::MenuCharacterCreation { action } => { format!("MenuCharacterCreation_{}", action.to_string()) } } } } impl StringToEnum for InteractionCode { fn from_string(s: &str) -> Option<Self> { let parts: Vec<&str> = s.split('_').collect(); if parts.len() != 2 { return None; } match parts[0] { "MenuCharacterCreation" => { CharacterCreationAction::from_string(parts[1]).map(|action| { InteractionCode::MenuCharacterCreation { action } }) } _ => None, } } } // 测试示例 fn main() { let code = InteractionCode::MenuCharacterCreation { action: CharacterCreationAction::Open }; let serialized = code.to_string(); assert_eq!(serialized, "MenuCharacterCreation_Open"); let parsed = InteractionCode::from_string(&serialized).unwrap(); assert_eq!(parsed, code); }
内容的提问来源于stack exchange,提问作者Iron_Teapot
相关产品推荐
相关产品推荐

