Comparing Values
Recipe | Crates |
---|---|
Test for Equality with PartialEq and Eq | |
Compare and Sort Values with PartialOrd and Ord |
Test for Equality with PartialEq
and Eq
The std::cmp
↗ module provides traits for comparing values and implementing algorithms that require ordering or equality checks.
PartialEq
↗ is used for types that can be checked for equality (using==
and!=
).Eq
↗ is a marker trait that indicates a type has a reflexive equality relation, meaninga == a
is always true. It requiresPartialEq
to be implemented first.- Floats (like
f32
↗ andf64
↗) do not implementEq
, becauseNaN
↗ !=NaN
. Beware when implementing equality on structs with a field of float type.
PartialEq
and Eq
are most often automatically derived using #[derive(PartialEq, Eq)]
- see Derive. You can however provide a custom implementation, if so desired:
// `#[derive(PartialEq, Eq)]` automatically implements equality traits. // When derived on structs, two instances are equal if all fields are equal, // and not equal if any fields are not equal. // When derived on enums, two instances are equal if they are the same variant // and all fields are equal. #[derive(Debug, PartialEq, Eq)] struct Point { x: i32, y: i32, } fn points() { let p1 = Point { x: 1, y: 2 }; let p2 = Point { x: 1, y: 2 }; let p3 = Point { x: 3, y: 4 }; println!("p1 == p2: {}", p1 == p2); // true println!("p1 == p3: {}", p1 == p3); // false } #[derive(Debug)] struct Person { id: u64, last_name: String, } // Custom implementation of `PartialEq` and `Eq`: impl PartialEq for Person { fn eq(&self, other: &Self) -> bool { self.id == other.id // `last_name` is ignored. } } // `Eq` has no methods. impl Eq for Person {} fn people() { let p1 = Person { id: 1, last_name: "Smith".to_string(), }; let p2 = Person { id: 2, last_name: "Smith".to_string(), }; assert_ne!(p1, p2); } fn main() { points(); people(); }
Compare and Sort Values with PartialOrd
and Ord
PartialOrd
↗ is used for types that can be compared for ordering, but may not have a total order.
Ord
↗ is used for types that have a total order, meaning every pair of values can be compared. Ord
requires that the implementing type also be PartialOrd
, PartialEq
↗, and Eq
↗.
Both traits can be automatically implemented with #[derive(...)]
. When writing a custom implementation, it is recommended to read the documentation for Ord
and PartialOrd
to avoid logic errors: Ord
must be consistent with the PartialOrd
implementation, and PartialOrd
with PartialEq
.
Sorting algorithms often rely on the PartialOrd
and Ord
trait to determine the order of elements. The standard library provides several sorting functions that use these traits, such as sort()
↗ and sort_by()
↗ on slices.
The following example implements a custom order for software versions:
use std::cmp::Ordering; // A struct to represent a software version. // The `Eq` trait has no methods, thus we can derive it automatically. #[derive(Debug, Eq)] struct Version { major: u32, minor: u32, patch: u32, } // Implement `PartialEq` to define when two versions are equal. impl PartialEq for Version { fn eq(&self, other: &Self) -> bool { self.major == other.major && self.minor == other.minor && self.patch == other.patch } } // Implement `PartialOrd`. Since our ordering is total, `partial_cmp` // can simply wrap the result of `cmp` in `Some`. impl PartialOrd for Version { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } } // Implement `Ord` to define the total ordering of versions. // It implements a lexicographical comparison. impl Ord for Version { fn cmp(&self, other: &Self) -> Ordering { // Compare major versions first. match self.major.cmp(&other.major) { Ordering::Equal => { // If major versions are equal, compare minor versions. match self.minor.cmp(&other.minor) { Ordering::Equal => { // If minor versions are also equal, compare patch // versions. self.patch.cmp(&other.patch) } // If minor versions differ, that's our result. other_ordering => other_ordering, } } // If major versions differ, that's our result. other_ordering => other_ordering, } } } fn main() { let v1 = Version { major: 1, minor: 2, patch: 3, }; let v2 = Version { major: 1, minor: 3, patch: 0, }; let v3 = Version { major: 2, minor: 0, patch: 0, }; // We can sort a collection of versions: let mut versions = vec![v3, v1, v2]; println!("Before sorting: {versions:?}"); versions.sort(); // `sort()` requires the `Ord` trait. println!("After sorting: {versions:?}"); // The sorted order should be [1.2.3, 1.3.0, 2.0.0]. assert_eq!( versions[0], Version { major: 1, minor: 2, patch: 3 } ); assert_eq!( versions[1], Version { major: 1, minor: 3, patch: 0 } ); assert_eq!( versions[2], Version { major: 2, minor: 0, patch: 0 } ); }
Related Topics
- Algorithms.
- Data Structures.
- Derive.
- Generics.
- Sorting.
- Traits.