如果我不在乎枚举字段,如何断言枚举是特定变量?

时间:2018-07-01 07:56:21

标签: rust

我现在想在测试中检查带有字段的枚举,而暂时忽略字段的实际值。

考虑以下示例:

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

我想在这里使用assert_eq!,但是编译器告诉我:

error: expected expression, found `}`
  --> src/lib.rs:18:64
   |
18 |         assert_eq!(return_with_fields(), MyEnum::WithFields {..});
   |                                                                ^ expected expression

这类似于Why do I get an error when pattern matching a struct-like enum variant with fields?,但该解决方案不适用于我的情况。

当然,我可以自己使用match,但是可以使用assert_eq!的工作会更少。

3 个答案:

答案 0 :(得分:4)

一个简单的解决方案是做相反的断言:

assert!(return_with_fields() != MyEnum::WithoutFields);

或更简单:

assert_ne!(return_with_fields(), MyEnum::WithoutFields);

当然,如果您的枚举中有更多成员,则必须添加更多断言来涵盖所有可能的情况。

或者,这可能是OP所考虑的,因为assert!只是在失败的情况下出现紧急情况,因此测试可以使用模式匹配,并在出现问题时直接调用panic!

match return_with_fields() {
    MyEnum::WithFields {..} => {},
    MyEnum::WithoutFields => panic!("expected WithFields, got WithoutFields"),
}

答案 1 :(得分:2)

您的原始代码可以与新的宏一起使用:

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 {..}));
}

我个人倾向于为枚举添加方法:

fn is_with_fields(&self) -> bool {
    match self {
        MyEnum::WithFields { .. } => true,
        _ => false,
    }
}

我也倾向于避免使用类似struct的枚举,而是投入额外的工作:

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

我希望有一天,可以将枚举变量制成自己的类型,以避免这种额外的结构。

答案 2 :(得分:0)

我会使用像@Shepmaster建议的宏,但是会报告更多错误(例如现有的assert!assert_eq!宏:

macro_rules! assert_variant {
    ($value:expr, $pattern:pat) => ({
        let value = &$value;

        if let $pattern = value {} else {
            panic!(r#"assertion failed (value doesn't match pattern):
   value: `{:?}`,
 pattern: `{}`"#, value, stringify!($pattern))
        }
    })

    // TODO: Additional patterns for trailing args, like assert and assert_eq
}

Rust playground demonstrating this example