Module comparator

Module comparator 

Source
Expand description

§Comparator Abstraction

Provides a Rust implementation similar to Java’s Comparator interface for comparison operations and chaining.

§Design Overview

This module adopts the Trait + Multiple Implementations design pattern, which is the most flexible and elegant approach for implementing comparators in Rust. It achieves a perfect balance between semantic clarity, type safety, and API flexibility.

§Core Components

  1. Comparator<T> trait: A minimalist unified interface that only defines the core compare method and type conversion methods (into_*). It does NOT include chaining methods like then_comparing, etc.

  2. Three Concrete Struct Implementations:

    • BoxComparator<T>: Box-based single ownership implementation for one-time use scenarios
    • ArcComparator<T>: Arc-based thread-safe shared ownership implementation for multi-threaded scenarios
    • RcComparator<T>: Rc-based single-threaded shared ownership implementation for single-threaded reuse
  3. Specialized Composition Methods: Each struct implements its own inherent methods (reversed, then_comparing, etc.) that return the same concrete type, preserving their specific characteristics (e.g., ArcComparator compositions remain ArcComparator and stay cloneable and thread-safe).

  4. Extension Trait for Closures: The FnComparatorOps<T> extension trait provides composition methods for all closures and function pointers, returning BoxComparator<T> to initiate method chaining.

  5. Unified Trait Implementation: All closures and the three structs implement the Comparator<T> trait, enabling them to be handled uniformly by generic functions.

§Ownership Model Coverage

The three implementations correspond to three typical ownership scenarios:

TypeOwnershipClonableThread-SafeAPIUse Case
BoxComparatorSingleconsumes selfOne-time
ArcComparatorSharedborrows &selfMulti-thread
RcComparatorSharedborrows &selfSingle-thread

§Key Design Advantages

§1. Type Preservation through Specialization

By implementing composition methods on concrete structs rather than in the trait, each type maintains its specific characteristics through composition:

use prism3_function::comparator::{Comparator, ArcComparator};
use std::cmp::Ordering;

let arc_cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
let another = ArcComparator::new(|a: &i32, b: &i32| b.cmp(a));

// Composition returns ArcComparator, preserving clonability and
// thread-safety
let combined = arc_cmp.then_comparing(&another);
let cloned = combined.clone();  // ✅ Still cloneable

// Original comparators remain usable
assert_eq!(arc_cmp.compare(&5, &3), Ordering::Greater);

§2. Elegant API without Explicit Cloning

ArcComparator and RcComparator use &self in their composition methods, providing a natural experience without requiring explicit .clone() calls:

use prism3_function::comparator::{Comparator, ArcComparator};

let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));

// No need for explicit clone()
let reversed = cmp.reversed();
let chained = cmp.then_comparing(&ArcComparator::new(|a, b| b.cmp(a)));

// cmp is still available
cmp.compare(&1, &2);

§3. Efficient Closure Composition

The FnComparatorOps extension trait allows direct composition on closures:

use prism3_function::comparator::{Comparator, FnComparatorOps};
use std::cmp::Ordering;

let cmp = (|a: &i32, b: &i32| a.cmp(b))
    .reversed()
    .then_comparing(|a: &i32, b: &i32| b.cmp(a));

assert_eq!(cmp.compare(&5, &3), Ordering::Less);

§Usage Examples

§Basic Comparison

use prism3_function::comparator::{Comparator, BoxComparator};
use std::cmp::Ordering;

let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
assert_eq!(cmp.compare(&5, &3), Ordering::Greater);

§Reversed Comparison

use prism3_function::comparator::{Comparator, BoxComparator};
use std::cmp::Ordering;

let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
let rev = cmp.reversed();
assert_eq!(rev.compare(&5, &3), Ordering::Less);

§Chained Comparison

use prism3_function::comparator::{Comparator, BoxComparator};
use std::cmp::Ordering;

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

let by_name = BoxComparator::new(|a: &Person, b: &Person| {
    a.name.cmp(&b.name)
});
let by_age = BoxComparator::new(|a: &Person, b: &Person| {
    a.age.cmp(&b.age)
});
let cmp = by_name.then_comparing(by_age);

let p1 = Person { name: "Alice".to_string(), age: 30 };
let p2 = Person { name: "Alice".to_string(), age: 25 };
assert_eq!(cmp.compare(&p1, &p2), Ordering::Greater);

§Author

Haixing Hu

Structs§

ArcComparator
An Arc-based thread-safe comparator with shared ownership.
BoxComparator
A boxed comparator with single ownership.
RcComparator
An Rc-based single-threaded comparator with shared ownership.

Traits§

Comparator
A trait for comparison operations.
FnComparatorOps
Extension trait providing composition methods for closures and function pointers.