How to check enum with values - rust

How do I check if a vector of enum has a certain member with values?
#[derive(PartialEq,Clone,Copy)]
enum TestResult {
Pass,
Fail {point: u8},
}
impl TestResult {
fn is_fail(&self) -> bool {
match *self {
TestResult::Fail{point:_} => true,
_ => false,
}
}
}
fn main() {
let v1 = vec![TestResult::Pass,
TestResult::Pass,
TestResult::Fail{point:50}];
if v1.contains(&TestResult::Pass) {
println!("result contains Pass");
}
if v1.iter().any(|&r| r.is_fail()) {
println!("result contains Fail");
}
}
This is working but is there a way to do this with Vec::contains()?
I want to check if TestResult::Fail is in a vector in the same way as for TestResult::Pass (no pattern matching. easy..)

There isn't really an easy way to do this, however we can make a simple macro to perform some pattern matching in the if statement.
/// Performs pattern matching contains on an IntoIter type
macro_rules! matches_any {
($iterable:expr, $($tokens:tt)+) => {{
let iter = ::std::iter::IntoIterator::into_iter($iterable);
iter.any(|x| matches!(x, $($tokens)+))
}};
}
The trick here is that we can use the matches! macro instead of a full match statement if all we want to know is if something matches a given pattern.
// Without matches!
match self {
TestResult::Fail {..} => true,
_ => false,
}
// With matches!
matches!(self, TestResult::Fail {..})
So now we can use this macro instead:
let v1 = vec![TestResult::Pass,
TestResult::Pass,
TestResult::Fail { point: 50 }];
if matches_any!(&v1, TestResult::Pass) {
println!("result contains Pass");
}
if matches_any!(&v1, TestResult::Fail {..}) {
println!("result contains Fail");
}

Related

in expressions, `_` can only be used on the left-hand side of an assignment

I've the following code I implemented to return an error when a string doesn't contain a match to something like "Mark, 55" to return an error. The compiler complains about my code Err(error) => Err(ParsePersonError::ParseInt(_))
use std::num::ParseIntError;
use std::str::FromStr;
#[derive(Debug, PartialEq)]
struct Person {
name: String,
age: usize,
}
// We will use this error type for the `FromStr` implementation.
#[derive(Debug, PartialEq)]
enum ParsePersonError {
// Empty input string
Empty,
// Incorrect number of fields
BadLen,
// Empty name field
NoName,
// Wrapped error from parse::<usize>()
ParseInt(ParseIntError),
}
// My implementation
impl FromStr for Person {
type Err = ParsePersonError;
fn from_str(s: &str) -> Result<Person, Self::Err> {
if s.len() == 0 {
Err(ParsePersonError::Empty)
}
else {
let v: Vec<&str> = s.split(",").collect();
println!("{:?}",v);
if &v[0]== &""{
Err(ParsePersonError::NoName)
}
else if v.len()!=2 {
Err(ParsePersonError::BadLen)
}
else {
let num = match v[1].parse::<usize>() {
Ok(n) => {
let name = v[0].to_string();
let age = n;
return Ok(Person {name,age})
},
Err(error) => Err(ParsePersonError::ParseInt(_))
};
Err(ParsePersonError::ParseInt(_))
}
}
}
}
However, the compiler doesn't complain about the same code in my test case.
ParsePersonError::ParseInt(_))
#[test]
fn missing_name_and_age() {
assert!(matches!(
",".parse::<Person>(),
Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
));
}
What am I missing?
If you de-sugar the match macro in test, it is something like
let result = ",".parse::<Person>();
let r = match result {
Err(ParsePersonError::NoName) => true,
Err(ParsePersonError::ParseInt(_)) => true,
_ => false,
};
assert!(r);
Essentially the '_' is to hold the value attached to ParseInt. Hence an assignment. This is what the compiler error message means.

Is there a macro I can use to expect a variant of an enum and extract its data?

Given an enum like
struct Earth { water: usize }
struct Mars { redness: usize }
enum World {
Mars(Mars),
Earth(Earth),
}
A common pattern I write is
fn something_expecting_mars(planet: World) {
let mars = match planet {
World::Mars(data) => data,
_ => panic!("Shouldn't be here now"),
}
}
Is there a macro I can use to expect a variant of an enum and subsequently extract its data?
// rewriting to this
let mars = expect_v!(planet, World::Mars);
The standard library provides a macro for testing a match, but not one for extracting a value. However, it's fairly easy to write one:
macro_rules! expect_v {
($e:expr, $p:path) => {
match $e {
$p(value) => value,
_ => panic!("expected {}", stringify!($p)),
}
};
}
Playground
As suggested in answers to the related question brought up in the comments, you might want to decouple value extraction from the panic. In that case, return an Option instead and let the callers panic if they wish by calling unwrap():
macro_rules! extract {
($e:expr, $p:path) => {
match $e {
$p(value) => Some(value),
_ => None,
}
};
}
// ...
fn something_expecting_mars(planet: World) {
let mars = extract!(planet, World::Mars).unwrap();
}
Anything wrong with just using if let instead of match?
mars = if let World::Mars(data) = planet { data } else { panic!("Woot woot")}

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

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.
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.
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"),
}
I'd use a macro like #Shepmaster proposed, but with more error reporting (like the existing assert! and assert_eq! macros:
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

Match an owned variable, then get access to the variable again

I want to do something along the lines of
enum A {
Type1 {
s: String
// ... some more fields
}
// ... some more variants
}
impl A {
fn consume(self) { println!("Consumed!"); }
}
fn fails() {
let b = A::Type1 { s: String::from("Arbitrary string") };
match b {
A::Type1 {
s, // (value moved here)
// ... more fields
} => {
let l = s.len(); // Something using the field from the enum
if l > 3 {
s.into_bytes(); // do something that requires ownership of s
} else {
b.consume(); // Value used here after move
}
}
// ... more cases
}
}
However, because I destructure b in the match case, I don't have access to it within the body of the match.
I can reconstruct b from the fields, but when b has lots of fields this is obviously not ideal. Is there any way to get around this issue without having to rebuild b?
No, you (almost literally) cannot have your cake and eat it too. Once you have destructured the value, the value no longer exists.
When non-lexical lifetimes happens, you can use a combination of NLL and match guards to prevent taking ownership in the first place:
#![feature(nll)]
enum A {
Type1 { s: String },
}
impl A {
fn consume(self) {
println!("Consumed!");
}
}
fn main() {
let b = A::Type1 {
s: String::from("Arbitrary string"),
};
match b {
A::Type1 { ref s } if s.len() <= 3 => {
b.consume();
}
A::Type1 { s } => {
s.into_bytes();
}
}
}

How to pattern match a String in a struct against a literal

In my code below I find that the code in match_num_works() has a certain elegance. I would like to write a String match with a similar formulation but cannot get it to work. I end up with match_text_works() which is less elegant.
struct FooNum {
number: i32,
}
// Elegant
fn match_num_works(foo_num: &FooNum) {
match foo_num {
&FooNum { number: 1 } => (),
_ => (),
}
}
struct FooText {
text: String,
}
// Clunky
fn match_text_works(foo_text: &FooText) {
match foo_text {
&FooText { ref text } => {
if text == "pattern" {
} else {
}
}
}
}
// Possible?
fn match_text_fails(foo_text: &FooText) {
match foo_text {
&FooText { text: "pattern" } => (),
_ => (),
}
}
Its probably not "elegant" or any nicer.. but one option is to move the conditional into the match expression:
match foo_text {
&FooText { ref text } if text == "pattern" => (),
_ => ()
}
Working sample: Playpen link.
Note that your desired pattern would actually work with a &str. You can't directly pattern match a String because it's a more complex value that includes an unexposed internal buffer.
struct FooText<'a> {
text: &'a str,
_other: u32,
}
fn main() {
let foo = FooText { text: "foo", _other: 5 };
match foo {
FooText { text: "foo", .. } => println!("Match!"),
_ => println!("No match"),
}
}
Playground

Resources