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.