Question
How can I filter a Rust iterator by excluding items whose enum field is Unknown without writing a verbose match expression?
For example, this works:
items.filter(|my_struct| match my_struct.my_enum {
MyEnum::Unknown => false,
_ => true,
})
But I would prefer an expression such as:
items.filter(|my_struct| my_struct.my_enum != MyEnum::Unknown)
This produces the error:
binary operation `!=` cannot be applied to type `MyEnum`
What is the idiomatic Rust alternative, and how can an enum support != comparisons?
Short Answer
You will learn why Rust enums do not automatically support == and !=, how to enable those operators with PartialEq, and when the matches! macro is clearer for checking enum variants in iterator filters.
Concept
Rust only allows == and != for types that implement the PartialEq trait. An enum does not receive that implementation automatically, even when it looks easy to compare.
For an enum you own, the usual solution is to derive PartialEq:
#[derive(PartialEq)]
enum MyEnum {
Unknown,
Active,
Disabled,
}
After that, variants can be compared directly:
if value.my_enum != MyEnum::Unknown {
// The value is not unknown.
}
For the specific task of asking whether a value has a particular variant, Rust also provides matches!:
!matches!(value.my_enum, MyEnum::Unknown)
matches! evaluates to true when a pattern matches. The ! negates that result, so it means “the enum is not Unknown.”
Pattern matching remains important when you need to extract data from a variant or handle multiple cases differently. For a simple yes/no variant check, PartialEq or matches! is usually more concise.
Mental Model
Think of an enum as a labeled card. Each card has one label, such as Unknown, Active, or Disabled.
PartialEqgives Rust permission to compare two cards and ask, “Do these have the same label and contents?”matches!asks, “Does this card have this particular label?”
Use comparison when comparing against a value. Use matches! when the important question is the shape or variant of the value.
Syntax and Examples
To use == or !=, derive PartialEq on the enum:
#[derive(Debug, PartialEq)]
enum Status {
Unknown,
Ready,
Failed,
}
struct Record {
status: Status,
}
fn main() {
let records = vec![
Record { status: Status::Unknown },
Record { status: Status::Ready },
Record { status: Status::Failed },
];
let known: Vec<&Record> = records
.iter()
.filter(|record| record.status != Status::Unknown)
.collect();
println!("{} known records", known.len());
}
iter() yields references to Record, so the closure parameter is effectively a &Record. The filter keeps records for which the condition is true.
The equivalent variant-checking form uses matches!:
let known: Vec<&Record> = records
.iter()
.filter(|record| !matches!(record.status, Status::Unknown))
.collect();
When writing enum variants outside the enum's defining scope, qualify them as Status::Unknown. You can also import variants with use Status::*;, but qualification is often clearer.
Step by Step Execution
Consider this filter:
let known: Vec<&Record> = records
.iter()
.filter(|record| record.status != Status::Unknown)
.collect();
Given these records:
let records = vec![
Record { status: Status::Unknown },
Record { status: Status::Ready },
Record { status: Status::Failed },
];
Execution proceeds as follows:
records.iter()visits eachRecordby reference. It does not consume the vector.- For the first record,
record.status != Status::Unknownisfalse, sofilterdiscards it. - For the second record,
Status::Ready != Status::Unknownistrue, sofilterkeeps it. - For the third record,
Status::Failed != Status::Unknownistrue, sofilterkeeps it. collect()stores the kept references inknown.
The final known vector contains the Ready and Failed records.
Real World Use Cases
Enum filtering is common when programs model states, outcomes, or categories.
- API processing: Ignore results with a
PendingorUnknownstatus before displaying completed data. - Validation: Keep only records whose state is not
Invalid. - Job queues: Process jobs with
Queuedstatus while skippingCancelledjobs. - Parsing: Filter out tokens such as
Whitespacebefore later compiler stages. - Monitoring: Select events that are not
Ignoredbefore sending alerts.
For a simple unit-like variant such as Unknown, both != and !matches! express this intent clearly.
Real Codebase Usage
In production Rust code, choose the clearest form for the job.
Derive equality for ordinary comparisons
#[derive(Debug, PartialEq, Eq)]
enum JobState {
Queued,
Running,
Completed,
Cancelled,
}
fn can_start(state: JobState) -> bool {
state == JobState::Queued
}
Eq is commonly derived together with PartialEq when every value can be compared for full equality. For ordinary enums whose fields also implement Eq, this is usually appropriate.
Use a guard clause for an excluded state
fn process(job: &Job) {
if job.state == JobState::Cancelled {
return;
}
// Continue processing a non-cancelled job.
}
Use matches! for grouped variants
let should_retry = matches!(result, JobResult::TemporaryError(_) | JobResult::TimedOut);
This is where matches! is particularly useful: a comparison cannot naturally express “one of these variants,” while a pattern can.
Use filter for selection, then map for transformation
let active_names: Vec<&str> = users
.iter()
.filter(|user| user.status == Status::Active)
.map(|user| user.name.as_str())
.collect();
This separates the selection rule from the transformation that follows it.
Common Mistakes
Forgetting PartialEq
This causes the error in the question:
// Missing #[derive(PartialEq)]
enum MyEnum {
Unknown,
Active,
}
// Error: `!=` is unavailable.
Fix it by deriving the trait:
#[derive(PartialEq)]
enum MyEnum {
Unknown,
Active,
}
Using an unqualified variant name
This may fail if Unknown is not in scope:
record.status != Unknown
Prefer the explicit form:
record.status != Status::Unknown
Returning the wrong boolean from filter
filter keeps values when its predicate returns true.
// This keeps only Unknown records, not known ones.
.filter(|record| record.status == Status::Unknown)
To exclude Unknown, use != or negate matches!.
Moving data out of a borrowed item
If an enum contains non-Copy data and you try to bind that data while iterating by reference, Rust may reject a move:
#[derive(PartialEq)]
enum Response {
Error(String),
Ok,
}
// `message` would attempt to move a String from a borrowed response.
// .filter(|response| matches!(response.kind, Response::Error(message)))
When only checking the variant, use _ instead:
.filter(|response| matches!(response.kind, Response::Error(_)))
If you need the inner data, match on a reference, such as match &response.kind.
Using match when a boolean expression is enough
This is valid but unnecessarily long for a simple exclusion:
.filter(|record| match record.status {
Status::Unknown => false,
_ => true,
})
Prefer:
.filter(|record| record.status != Status::Unknown)
or:
.filter(|record| !matches!(record.status, Status::Unknown))
Comparisons
| Approach | Best for | Example |
|---|---|---|
== / != with PartialEq | Comparing to one exact enum value | state != State::Unknown |
matches! | Checking one variant, patterns, or several variants | `matches!(state, State::Ready |
match | Different behavior or returned values for each case | match state { ... } |
if let | Handling one variant and ignoring the rest | if let State::Failed(reason) = state { ... } |
PartialEq comparisons are concise for exact equality. matches! becomes more expressive when variants carry data or multiple patterns should count as a match. A full match is best when every branch needs distinct logic.
Cheat Sheet
// Enable == and != for an enum you own.
#[derive(PartialEq)]
enum Status {
Unknown,
Active,
}
let is_active = status == Status::Active;
let is_known = status != Status::Unknown;
// Test whether a value has a variant.
let is_unknown = matches!(status, Status::Unknown);
let is_known = !matches!(status, Status::Unknown);
// Match multiple variants.
let can_run = matches!(status, Status::Active | Status::Queued);
// Filter an iterator.
let known: Vec<&Record> = records
.iter()
.filter(|record| record.status != Status::Unknown)
.collect();
filterretains values only when its closure returnstrue.- Use
EnumName::Variantunless the variant was explicitly imported. - Derive
Eqtoo when all enum fields support full equality:
#[derive(PartialEq, Eq)]
FAQ
Why does my Rust enum not support !=?
The != operator requires the PartialEq trait. Add #[derive(PartialEq)] to an enum you own, provided all of its fields can also be compared.
Should I use matches! or == for an enum variant?
Use == when equality is the idea you want to communicate. Use matches! when checking patterns, matching several variants, or ignoring data inside a variant.
Does matches! return a boolean?
Yes. matches!(value, pattern) returns true if value matches pattern; otherwise it returns false.
How do I exclude one enum variant in Iterator::filter?
Use either of these forms:
.filter(|item| item.status != Status::Unknown)
.filter(|item| !matches!(item.status, Status::Unknown))
Why should I write Status::Unknown instead of Unknown?
Enum variants are namespaced under their enum type. Status::Unknown works without importing the variant and makes the code's meaning clearer.
Can I derive PartialEq for an enum with data?
Yes, if every field in every variant implements PartialEq.
#[derive(PartialEq)]
enum ResultValue {
Number(i32),
Message(String),
}
When is a full match still necessary?
Use match when each variant needs different code, when you need to extract associated data, or when Rust should verify that every possible variant is handled.
Mini Project
Description
Build a small record filter that removes entries with an unknown status. This mirrors common application code where incomplete or unclassified data should not appear in a report.
Goal
Filter a list of device readings so that only readings with a known status are displayed.
Requirements
Define a ReadingStatus enum with Unknown, Normal, and Warning variants.
Create a Reading struct containing a device name and status.
Create several readings, including at least one with Unknown status.
Use an iterator filter to exclude unknown readings.
Print the remaining device names and statuses.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.