Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I assert an enum is a specific variant if I don't care about its fields?

Tags:

rust

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.

like image 264
Philipp Ludwig Avatar asked Jul 01 '18 07:07

Philipp Ludwig


People also ask

What is enum variant?

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), }

Why is enum not good?

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.

Can you use an enum as a type?

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');


2 Answers

Rust 1.42

You can use std::matches:

assert!(matches!(return_with_fields(), MyEnum::WithFields { .. })); 

Previous versions

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.

like image 200
Shepmaster Avatar answered Oct 21 '22 03:10

Shepmaster


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"), } 
like image 32
SirDarius Avatar answered Oct 21 '22 02:10

SirDarius