Sling Academy
Home/Rust/Rust: Implementing partial equality or ordering for custom vector or map elements

Rust: Implementing partial equality or ordering for custom vector or map elements

Last updated: January 07, 2025

When working with Rust, a powerful systems programming language known for its safety and concurrency features, you might encounter situations where you need to implement partial equality or ordering for custom elements within collections like vectors or maps. Rust offers traits such as PartialEq and PartialOrd to help achieve this functionality.

Understanding PartialEq and PartialOrd

Before diving into examples, let’s quickly review what PartialEq and PartialOrd are:

  • PartialEq: Used to specify custom equality between two objects. Implementing this trait allows you to use the == and != operators.
  • PartialOrd: Allows for custom ordering between two objects. This enables you to use operators such as <, >, <=, and >=.

Let’s explore how you can implement these traits for custom data types used within vectors or maps.

Implementing PartialEq

Suppose you have a custom data type called Person with fields name and age. We want to say that two Person objects are equal if they have the same name and age.


#[derive(Debug)]
struct Person {
    name: String,
    age: u8,
}

impl PartialEq for Person {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.age == other.age
    }
}

With the above implementation, you can now check for equality in a vector:


fn main() {
    let mut people = vec![
        Person { name: String::from("Alice"), age: 30 },
        Person { name: String::from("Bob"), age: 25 },
    ];
    
    let alice = Person { name: String::from("Alice"), age: 30 };
    assert!(people.contains(&alice));
}

This implementation allows using .contains() to check if the vector includes a Person with matching fields.

Implementing PartialOrd

Now, suppose you want to sort Person objects by their age. You can implement PartialOrd like this:


impl PartialOrd for Person {
    fn partial_cmp(&self, other: &Self) -> Option {
        Some(self.age.cmp(&other.age))
    }
}

In this example, we use the cmp function from the standard library to facilitate age comparison. partial_cmp must return an Option of Ordering, and we wrap the result of cmp in Some.

With this implemented, you can now sort a Vec<Person>:


fn main() {
    let mut people = vec![
        Person { name: String::from("Alice"), age: 30 },
        Person { name: String::from("Bob"), age: 25 },
    ];
    
    people.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
    
    for person in &people {
        println!("{:?}", person);
    }
}

You can use partial_cmp directly in the sort_by closure to order the people by age.

Handling Complex Logic

If your equality or ordering logic is more complex, you still follow the same pattern within these traits, defining exactly how elements should be ordered or checked for equality according to your specific criteria.

For example, if equality depended on a specific subset or transformation of fields, you'd implement that logic within the eq or partial_cmp methods.

Final Thoughts

Implementing PartialEq and PartialOrd is essential when you deal with collections of custom structs, allowing you to define meaningful semantics for equality and ordering. With these implementations in place, Rust's powerful collections library becomes even more flexible for your custom types.

Next Article: Rust: Immutably sharing vectors with Arc> across threads

Previous Article: Rust: Serializing and deserializing HashMaps and Vectors with Serde

Series: Collections 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