Rust测试中仅匹配单个枚举变体的优雅解决办法
问题场景
先看这段Rust异步测试代码:
pub enum State { Started(Vec<Id>), InProgress, Terminated } pub trait Locker { async fn start(&mut self, id: Id) -> State; } #[tokio::test] async fn test_locker() { let State::Started(ids) = locker.start().await; }
编译器会提示test_locker未处理所有枚举匹配项,但开发者明确该测试中locker.start().await只会返回State::Started变体,不想用match忽略其他分支,希望找更优雅的写法。
之前尝试的错误写法:
let State::Started(ids) = locker.start().await; assert!(matches!(ids, State::Started(ids)));
这里的问题是第一行解构出的ids是Vec<Id>类型,不是State枚举,断言逻辑完全错误。另外提前声明let ids: Vec<Id>也会因为作用域问题无法在解构时复用变量名。
可行解决方案
方案1:给枚举加专属解构方法
给State实现一个方法,非Started变体时直接panic(测试场景下panic会让测试失败,符合预期):
impl State { fn into_started(self) -> Vec<Id> { match self { State::Started(ids) => ids, other => panic!("预期State::Started,实际得到{:?}", other), } } } #[tokio::test] async fn test_locker() { let ids = locker.start().await.into_started(); }
把匹配逻辑封装到枚举内部,测试代码更干净,panic时还会输出实际的变体值,方便调试。
方案2:if let + unreachable!()
不想加方法的话,用if let配合断言和unreachable!():
#[tokio::test] async fn test_locker() { let state = locker.start().await; // 先断言确保是目标变体 assert!(matches!(state, State::Started(_))); // 解构,else分支用unreachable!()兜底 let State::Started(ids) = state else { unreachable!(); }; // 后续正常使用ids }
unreachable!()会在代码意外走到这里时panic,测试失败时会明确提示“unreachable code reached”,结合前面的断言,能清晰表达测试预期。
方案3:自定义unwrap风格宏
如果多处需要这种解构,可以写个简单的宏:
macro_rules! unwrap_started { ($expr:expr) => { match $expr { State::Started(ids) => ids, other => panic!("预期Started变体,实际得到{:?}", other), } }; } #[tokio::test] async fn test_locker() { let ids = unwrap_started!(locker.start().await); }
宏可以让测试代码极度简洁,适合频繁使用的场景。
内容的提问来源于stack exchange,提问作者unsafe_where_true
相关产品推荐
相关产品推荐

