如何按包含类型而非分支解包Rust的either::Either?
按类型解包either::Either的实现方案
可以实现按目标类型而非左右分支来解包Either,无需关心值处于哪个分支。下面先分析你现有宏的问题,再给出两种可行的实现方式。
现有宏的问题
你写的宏存在多处语法和逻辑错误:
match分支格式错误,分支间不能用逗号分隔,且每个分支需用{}包裹if let写法错误,正确格式应为if let 模式 = 表达式,而非if let 模式 => 表达式- 试图通过
Either<$item_t, _>匹配类型,这在Rust的match模式中不被支持
解决方案一:使用Trait实现类型安全提取
这种方式更贴合Rust类型系统设计,编译时即可保证类型安全,无需依赖宏。
先定义三个Trait,分别用于提取不可变引用、可变引用和转移所有权:
use either::Either; /// 提取不可变引用 pub trait ExtractRef<T> { fn extract_ref(&self) -> Option<&T>; } /// 提取可变引用 pub trait ExtractMut<T> { fn extract_mut(&mut self) -> Option<&mut T>; } /// 提取所有权 pub trait Extract<T> { fn extract(self) -> Option<T>; } // 针对Left分支类型实现ExtractRef impl<A, B, T> ExtractRef<T> for Either<A, B> where A: AsRef<T>, { fn extract_ref(&self) -> Option<&T> { match self { Either::Left(val) => Some(val.as_ref()), Either::Right(_) => None, } } } // 针对Right分支类型实现ExtractRef impl<A, B, T> ExtractRef<T> for Either<A, B> where B: AsRef<T>, { fn extract_ref(&self) -> Option<&T> { match self { Either::Right(val) => Some(val.as_ref()), Either::Left(_) => None, } } } // 实现ExtractMut impl<A, B, T> ExtractMut<T> for Either<A, B> where A: AsMut<T>, { fn extract_mut(&mut self) -> Option<&mut T> { match self { Either::Left(val) => Some(val.as_mut()), Either::Right(_) => None, } } } impl<A, B, T> ExtractMut<T> for Either<A, B> where B: AsMut<T>, { fn extract_mut(&mut self) -> Option<&mut T> { match self { Either::Right(val) => Some(val.as_mut()), Either::Left(_) => None, } } } // 实现Extract(转移所有权) impl<A, B, T> Extract<T> for Either<A, B> where A: Into<T>, { fn extract(self) -> Option<T> { match self { Either::Left(val) => Some(val.into()), Either::Right(_) => None, } } } impl<A, B, T> Extract<T> for Either<A, B> where B: Into<T>, { fn extract(self) -> Option<T> { match self { Either::Right(val) => Some(val.into()), Either::Left(_) => None, } } }
使用示例
针对你配置解析场景中的TypeAOrTypeB结构体,可这样提取目标类型:
// 假设已有TypeA和TypeB的定义 let example: Example = /* 从YAML解析得到的实例 */; for (key, val) in example.dual_field { // 尝试提取TypeA if let Some(type_a) = val.inner.extract_ref::<TypeA>() { println!("Key {} 对应TypeA: {:?}", key, type_a); } // 尝试提取TypeB if let Some(type_b) = val.inner.extract_ref::<TypeB>() { println!("Key {} 对应TypeB: {:?}", key, type_b); } }
解决方案二:修复后的宏实现
如果你更倾向于用宏,下面是修复后的版本:
use either::Either; #[macro_export] macro_rules! unpack_dual { ($item:expr, $item_t:ty) => { match &$item.inner { Either::Left(val) => { let val: &$item_t = val; Some(*val) } Either::Right(_) => match &$item.inner { Either::Right(val) => { let val: &$item_t = val; Some(*val) } Either::Left(_) => None, }, } }; }
使用示例
let val: TypeAOrTypeB = /* 解析得到的值 */; let type_a: Option<TypeA> = unpack_dual!(val, TypeA); let type_b: Option<TypeB> = unpack_dual!(val, TypeB);
注意:该宏要求目标类型必须是Either的其中一个分支类型,否则会触发编译错误,保证了类型安全性。
内容的提问来源于stack exchange,提问作者Luctins
相关产品推荐
相关产品推荐

