如何通过Crate或宏实现Rust泛型类型的匹配?
问题描述
我发现可以通过以下方式对泛型类型进行「匹配」:
use std::any::Any; trait SomeTrait {} struct SomeStruct<T: SomeTrait> { atr: T, } struct Bar { name: String, } impl SomeTrait for Bar {} struct Baz; impl SomeTrait for Baz {} fn foo<T: SomeTrait + 'static>(s: SomeStruct<T>) { let any: Box<dyn Any> = Box::new(s.atr); if let Some(bar) = any.downcast_ref::<Bar>() { println!("got bar! {:?}", bar.name); } else { println!("did not get bar"); } } fn main() { foo::<Bar>(SomeStruct { atr: Bar { name: "hello".to_string() } }); foo::<Baz>(SomeStruct { atr: Baz {} }); }
请问是否有现成的Crate或宏可以实现该功能?我设想的宏使用方式如下:
fn foo<T: SomeTrait + 'static>(s: SomeStruct<T>) { generic_match! s.ty { Bar(bar) => println!("got bar! {:?}", bar.name), Baz => println!("did not get bar"), } }
解决方案
现成Crate推荐
downcast-rs:这个Crate简化了
Anytrait的向下转换逻辑,支持为自定义trait添加向下转换能力。你可以为trait SomeTrait实现Downcasttrait,之后就能直接对&dyn SomeTrait进行类型匹配,省去手动封装Box<dyn Any>的步骤。match-type:这个Crate提供的
match_type!宏语法和你设想的高度契合,能直接针对泛型类型做分支匹配,用法示例如下:
use match_type::match_type; fn foo<T: SomeTrait + 'static>(s: SomeStruct<T>) { match_type!(T { Bar => { let bar = &s.atr as &Bar; println!("got bar! {:?}", bar.name); }, Baz => println!("did not get bar"), _ => println!("unhandled type"), }); }
手动实现宏
如果不想引入额外依赖,也可以自己实现一个轻量宏,核心是自动生成downcast_ref的匹配分支:
macro_rules! generic_match { ($target:expr, { $($ty:ident($var:ident) => $body:block,)* $($ty2:ident => $body2:block,)* }) => { let any_ref = &$target as &dyn std::any::Any; $( if let Some($var) = any_ref.downcast_ref::<$ty>() { $body } else )* $( if any_ref.is::<$ty2>() { $body2 } else )* { // 默认分支,不需要可删除 unreachable!("unexpected type") } }; } // 使用示例 fn foo<T: SomeTrait + 'static>(s: SomeStruct<T>) { generic_match!(s.atr, { Bar(bar) => { println!("got bar! {:?}", bar.name); }, Baz => { println!("did not get bar"); }, }); }
内容的提问来源于stack exchange,提问作者0x5DA
相关产品推荐
相关产品推荐

