I'd like to check enums with fields in tests while ignoring the actual value of the fields for now.
Consider the following example:
enum MyEnum { WithoutFields, WithFields { field: String }, } fn return_with_fields() -> MyEnum { MyEnum::WithFields { field: "some string".into(), } } #[cfg(test)] mod tests { use super::*; #[test] fn example() { assert_eq!(return_with_fields(), MyEnum::WithFields {..}); } }
playground
I'd like to use assert_eq!
here, but the compiler tells me:
error: expected expression, found `}` --> src/lib.rs:18:64 | 18 | assert_eq!(return_with_fields(), MyEnum::WithFields {..}); | ^ expected expression
This is similar to Why do I get an error when pattern matching a struct-like enum variant with fields?, but the solution does not apply in my case.
Of course, I can use match
and do it myself, but being able to use assert_eq!
would be less work.
An enum in Rust is a type that represents data that is one of several possible variants. Each variant in the enum can optionally have data associated with it: #![allow(unused_variables)] fn main() { enum Message { Quit, ChangeColor(i32, i32, i32), Move { x: i32, y: i32 }, Write(String), }
The problem with enums is described in Fowler's Refactoring, where it is considered a code smell. It has nothing to do with type safety, but rather that it forces you to sprinkle switch statements all over your code, thus violating the DRY Principle.
Just like other data types in TypeScript, we can use enums as function parameters or return types, like this: enum Weekend { Friday = 1, Saturday, Sunday } function getDate(Day: string): Weekend { if ( Day === 'TGIF') { return Weekend.Friday; } } let DayType: Weekend = getDate('TGIF');
You can use std::matches
:
assert!(matches!(return_with_fields(), MyEnum::WithFields { .. }));
Your original code can be made to work with a new macro:
macro_rules! is_enum_variant { ($v:expr, $p:pat) => ( if let $p = $v { true } else { false } ); } #[test] fn example() { assert!(is_enum_variant!(return_with_fields(), MyEnum::WithoutFields {..})); }
Personally, I tend to add methods to my enums:
fn is_with_fields(&self) -> bool { match self { MyEnum::WithFields { .. } => true, _ => false, } }
I also tend to avoid struct-like enums and instead put in extra work:
enum MyEnum { WithoutFields, WithFields(WithFields), } struct WithFields { field: String } impl MyEnum { fn is_with_fields(&self) -> bool { match self { MyEnum::WithFields(_) => true, _ => false, } } fn as_with_fields(&self) -> Option<&WithFields> { match self { MyEnum::WithFields(x) => Some(x), _ => None, } } fn into_with_fields(self) -> Option<WithFields> { match self { MyEnum::WithFields(x) => Some(x), _ => None, } } }
I hope that some day, enum variants can be made into their own type to avoid this extra struct.
If you are using Rust 1.42 and later, see Shepmaster's answer below.
A simple solution here would be to do the opposite assertion:
assert!(return_with_fields() != MyEnum::WithoutFields);
or even more simply:
assert_ne!(return_with_fields(), MyEnum::WithoutFields);
Of course if you have more members in your enum, you'll have to add more asserts to cover all possible cases.
Alternatively, and this what OP probably had in mind, since assert!
just panics in case of failure, the test can use pattern matching and call panic!
directly in case something is wrong:
match return_with_fields() { MyEnum::WithFields {..} => {}, MyEnum::WithoutFields => panic!("expected WithFields, got WithoutFields"), }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With