Sling Academy
Home/Rust/Rust - Comparing Enums with PartialEq and Derivable Traits

Rust - Comparing Enums with PartialEq and Derivable Traits

Last updated: January 07, 2025

In Rust, enums or enumerations are a powerful feature that allow you to define a type by enumerating its possible variants. One common task when working with enums is comparing them. Rust provides traits like PartialEq and derivable traits to assist with this. In this article, we’ll explore how to use these to compare enums effectively in Rust.

Enums in Rust are defined using the enum keyword. Here's a simple example:

enum Direction {
    North,
    South,
    East,
    West,
}

By default, Rust cannot compare enum variants using operators like == or !=. To enable this, we can implement the PartialEq trait.

Understanding PartialEq Trait

The PartialEq trait is used to specify equality comparisons. You can implement it manually for your enums, but Rust provides a convenient way to derive it automatically in most cases.

Here's how you can derive the PartialEq trait for the Direction enum:

#[derive(PartialEq)]
enum Direction {
    North,
    South,
    East,
    West,
}

With PartialEq derived, you can now compare Direction values:

fn main() {
    let dir1 = Direction::North;
    let dir2 = Direction::South;
    let is_equal = dir1 == dir2; // false
    println!("Are directions equal? {}", is_equal);
}

This will print: Are directions equal? false. Deriving PartialEq simplifies equality comparisons, making your code concise and readable.

Using PartialOrd for Ordering Comparisons

If you need to compare not just equality but also the order between enum variants, consider using the PartialOrd and Ord traits. Like PartialEq, PartialOrd can be automatically derived with:

#[derive(PartialEq, PartialOrd)]
enum Importance {
    Low,
    Medium,
    High,
}

With this, you can compare them using <, >, <=, and >=:

fn main() {
    let imp1 = Importance::Low;
    let imp2 = Importance::High;
    let comparison = imp1 < imp2; // true
    println!("Is Low less than High? {}", comparison);
}

Is Low less than High? true will be printed since Low is defined before High in the enum.

Custom Comparison Behavior

Sometimes, the default comparison generated by PartialEq or PartialOrd might not be suitable. You can implement these traits manually to define custom comparison logic.

Consider these enums where you want custom behavior:

enum Color {
    Red,
    Green,
    Blue,
}

impl PartialEq for Color {
    fn eq(&self, other: &Self) -> bool {
        use Color::*;
        match (self, other) {
            (Red, Red) => true,
            (Green, Green) => true,
            (Blue, Blue) => true,
            (_, _) => false,
        }
    }
}

In the above example, custom equality is defined directly, although it mimics the default equality behavior.

Comparing Enums with Data

If an enum includes associative data, derived comparisons also extend to the containing data:

#[derive(PartialEq)]
enum Message {
    Text(String),
    Move{x: i32, y: i32},
    ChangeColor(u8, u8, u8),
}

You can compare instances of this enum:
let msg1 = Message::Text(String::from("hello")); let msg2 = Message::Text(String::from("world")); let are_msgs_equal = msg1 == msg2; // false

Each variant’s associated data is included in the comparison, allowing for rich comparison logic.

Conclusion

Rust's derived traits are a powerful feature for quickly enabling equality and ordering comparisons on enum types. While derived traits often cover basic needs, Rust is flexible enough to allow manual trait implementations for fine-tuned or complex comparison logic as your coding needs demand.

Next Article: Rust - Enum Exhaustiveness and Future-Proofing with `#[non_exhaustive]`

Previous Article: Rust - Debugging Enum Pattern Matching Mistakes and Mismatches

Series: Enum and Pattern Matching in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior