Comprehensive functional programming abstractions for Rust, providing Java-style functional interfaces with Rust's ownership models.
This crate provides a complete set of functional programming abstractions inspired by Java's functional interfaces, adapted to Rust's ownership system. It offers multiple implementations for each abstraction (Box/Arc/Rc) to cover various use cases from simple single-threaded scenarios to complex multi-threaded applications.
- Complete Functional Interface Suite: Predicate, Consumer, Supplier, ReadonlySupplier, Transformer, Mutator, BiConsumer, BiPredicate, BiTransformer, Comparator, and Tester
 - Multiple Ownership Models: Box-based single ownership, Arc-based thread-safe sharing, and Rc-based single-threaded sharing
 - Flexible API Design: Trait-based unified interface with concrete implementations optimized for different scenarios
 - Method Chaining: All types support fluent API and functional composition
 - Thread-Safety Options: Choose between thread-safe (Arc) and efficient single-threaded (Rc) implementations
 - Lock-Free Concurrency: ReadonlySupplier provides lock-free concurrent access for stateless scenarios
 - Zero-Cost Abstractions: Efficient implementations with minimal runtime overhead
 
Tests whether a value satisfies a condition, returning bool. Similar to Java's Predicate<T> interface.
test(&self, value: &T) -> bool- Tests if the value satisfies the predicate condition- Corresponds to 
Fn(&T) -> boolclosure 
BoxPredicate<T>: Single ownership, non-cloneableArcPredicate<T>: Thread-safe shared ownership, cloneableRcPredicate<T>: Single-threaded shared ownership, cloneable
- Logical composition: 
and,or,not,xor,nand,nor - Type-preserving method chaining (each returns the same concrete type)
 - Extension trait 
FnPredicateOpsfor closures - provides composition methods that returnBoxPredicate 
All logical composition methods (and, or, xor, nand, nor) accept the other parameter by value, which means:
- Ownership is transferred: The 
otherpredicate is consumed and becomes unavailable after the operation - To preserve the original: You must explicitly 
clone()it first (only works forArcPredicateandRcPredicate) BoxPredicatecannot be cloned: Once used in a composition, it's consumed
use prism3_function::{ArcPredicate, RcPredicate, BoxPredicate, Predicate};
// ArcPredicate and RcPredicate can be cloned
let is_even = ArcPredicate::new(|x: &i32| x % 2 == 0);
let is_positive = ArcPredicate::new(|x: &i32| *x > 0);
// Option 1: Clone to preserve the original
let combined = is_even.and(is_positive.clone());
// is_positive is still usable because we cloned it
assert!(is_positive.test(&2));
// Option 2: Use &self methods (for Rc/Arc only)
let is_even_rc = RcPredicate::new(|x: &i32| x % 2 == 0);
let is_positive_rc = RcPredicate::new(|x: &i32| *x > 0);
let combined_rc = is_even_rc.and(is_positive_rc.clone());
// Both predicates remain usable
assert!(is_even_rc.test(&2));
assert!(is_positive_rc.test(&2));
// BoxPredicate: Cannot be cloned, will be consumed
let box_pred = BoxPredicate::new(|x: &i32| *x > 0);
let combined_box = box_pred.and(|x: &i32| x % 2 == 0);
// box_pred is no longer available hereuse prism3_function::{ArcPredicate, Predicate, FnPredicateOps};
// Create predicates with logical composition
let is_even = ArcPredicate::new(|x: &i32| x % 2 == 0);
let is_positive = ArcPredicate::new(|x: &i32| *x > 0);
// Clone to preserve the original predicate
let is_even_and_positive = is_even.and(is_positive.clone());
assert!(is_even_and_positive.test(&4));
assert!(!is_even_and_positive.test(&3));
// is_positive is still usable
assert!(is_positive.test(&5));
// Use with closures - extension trait automatically provides composition
let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<i32> = numbers
    .into_iter()
    .filter(|x| (|n: &i32| *n > 2).and(|n: &i32| n % 2 == 0).test(x))
    .collect();Accepts a single input parameter and performs operations without returning a result. Similar to Java's Consumer<T>.
accept(&mut self, value: &T)- Performs an operation on the value reference- Corresponds to 
FnMut(&T)closure 
BoxConsumer<T>: Single ownership, usesFnMut(&T)ArcConsumer<T>: Thread-safe withArc<Mutex<>>, cloneableRcConsumer<T>: Single-threaded withRc<RefCell<>>, cloneableBoxConsumerOnce<T>: One-time use withFnOnce(&T)
and_then- Chains consumers sequentiallywhen- Conditional execution with predicate- Type conversions: 
into_box,into_arc,into_rc - Extension trait 
FnConsumerOpsfor closures 
ReadonlyConsumer- For pure observation without modifying consumer state
All composition methods (and_then, when, or_else) accept their parameters by value, which means:
- Ownership is transferred: The parameter (consumer or predicate) is consumed and becomes unavailable after the operation
 - To preserve the original: You must explicitly 
clone()it first (only works forArcConsumerandRcConsumer) - BoxConsumer cannot be cloned: Once used in a composition, it's consumed and no longer available
 
use prism3_function::{BoxConsumer, Consumer};
// Create a consumer for observation (not modification)
let mut consumer = BoxConsumer::new(|x: &i32| {
    println!("Observed value: {}", x);
});
let value = 10;
consumer.accept_once(&value);
// value is unchangeduse prism3_function::{BoxConsumer, Consumer};
use std::sync::{Arc, Mutex};
let log = Arc::new(Mutex::new(Vec::new()));
let log1 = log.clone();
let log2 = log.clone();
// Chain multiple consumers
let mut consumer = BoxConsumer::new(move |x: &i32| {
    log1.lock().unwrap().push(format!("First: {}", x));
})
.and_then(move |x: &i32| {
    log2.lock().unwrap().push(format!("Second: {}", x));
});
consumer.accept_once(&42);
assert_eq!(log.lock().unwrap().len(), 2);use prism3_function::{BoxConsumer, Consumer};
use std::sync::{Arc, Mutex};
let log = Arc::new(Mutex::new(Vec::new()));
let log_clone = log.clone();
// Only execute consumer when predicate is true
let mut consumer = BoxConsumer::new(move |x: &i32| {
    log_clone.lock().unwrap().push(*x);
})
.when(|x: &i32| *x > 0);  // Only log positive numbers
consumer.accept_once(&10);   // Logged
consumer.accept_once(&-5);   // Not logged
assert_eq!(log.lock().unwrap().len(), 1);use prism3_function::{BoxConsumer, Consumer};
use std::sync::{Arc, Mutex};
let log = Arc::new(Mutex::new(Vec::new()));
let log1 = log.clone();
let log2 = log.clone();
// Execute different consumers based on condition
let mut consumer = BoxConsumer::new(move |x: &i32| {
    log1.lock().unwrap().push(format!("Positive: {}", x));
})
.when(|x: &i32| *x > 0)
.or_else(move |x: &i32| {
    log2.lock().unwrap().push(format!("Non-positive: {}", x));
});
consumer.accept_once(&10);   // "Positive: 10"
consumer.accept_once(&-5);   // "Non-positive: -5"
assert_eq!(log.lock().unwrap().len(), 2);Modifies values in-place by accepting mutable references. Similar to Java's UnaryOperator<T> but with in-place modification.
mutate(&mut self, value: &mut T)- Modifies the value in-place- Corresponds to 
FnMut(&mut T)closure 
BoxMutator<T>: Single ownership, usesFnMut(&mut T)ArcMutator<T>: Thread-safe withArc<Mutex<>>, cloneableRcMutator<T>: Single-threaded withRc<RefCell<>>, cloneableBoxMutatorOnce<T>: One-time use withFnOnce(&mut T)
and_then- Chains mutators sequentiallywhen- Creates conditional mutator (if-then pattern)or_else- Adds else branch to conditional mutator (if-then-else pattern)- Type conversions: 
into_box,into_arc,into_rc - Extension trait 
FnMutatorOpsfor closures 
- Consumer: Accepts 
&T(reads values, doesn't modify input) - Mutator: Accepts 
&mut T(modifies values in-place) 
use prism3_function::{BoxMutator, Mutator};
// Create a mutator that modifies values
let mut mutator = BoxMutator::new(|x: &mut i32| *x *= 2)
    .and_then(|x: &mut i32| *x += 1);
let mut value = 10;
mutator.mutate(&mut value);
assert_eq!(value, 21); // (10 * 2) + 1
// Conditional mutator with if-then-else logic
let mut conditional = BoxMutator::new(|x: &mut i32| *x *= 2)
    .when(|x: &i32| *x > 0)
    .or_else(|x: &mut i32| *x -= 1);
let mut positive = 5;
conditional.mutate(&mut positive);
assert_eq!(positive, 10); // 5 * 2
let mut negative = -5;
conditional.mutate(&mut negative);
assert_eq!(negative, -6); // -5 - 1Generates values lazily without input parameters. Similar to Java's Supplier<T>.
get(&mut self) -> T- Generates and returns a value- Corresponds to 
FnMut() -> Tclosure 
BoxSupplier<T>: Single ownership, usesFnMut() -> TArcSupplier<T>: Thread-safe withArc<Mutex<>>, cloneableRcSupplier<T>: Single-threaded withRc<RefCell<>>, cloneableBoxSupplierOnce<T>: One-time use withFnOnce() -> T
map- Transforms supplier outputfilter- Filters supplier output with predicateflat_map- Chains suppliers- Factory methods: 
constant,counter - Type conversions: 
into_box,into_arc,into_rc 
use prism3_function::{BoxSupplier, Supplier};
// Create a counter supplier
let mut counter = {
    let mut count = 0;
    BoxSupplier::new(move || {
        count += 1;
        count
    })
};
assert_eq!(counter.get(), 1);
assert_eq!(counter.get(), 2);
assert_eq!(counter.get(), 3);
// Method chaining with map
let mut pipeline = BoxSupplier::new(|| 10)
    .map(|x| x * 2)
    .map(|x| x + 5);
assert_eq!(pipeline.get(), 25);Generates values lazily without input parameters and without modifying its own state. Unlike Supplier<T>, it uses &self instead of &mut self, enabling usage in read-only contexts and lock-free concurrent access.
get(&self) -> T- Generates and returns a value (note:&self, not&mut self)- Corresponds to 
Fn() -> Tclosure 
| Aspect | Supplier | ReadonlySupplier | 
|---|---|---|
| self signature | &mut self | 
&self | 
| Closure type | FnMut() -> T | 
Fn() -> T | 
| Can modify state | Yes | No | 
| Arc implementation | Arc<Mutex<FnMut>> | 
Arc<Fn> (lock-free!) | 
| Use cases | Counter, generator | Factory, constant, high concurrency | 
BoxReadonlySupplier<T>: Single ownership, zero overheadArcReadonlySupplier<T>: Lock-free thread-safe sharing (noMutexneeded!)RcReadonlySupplier<T>: Single-threaded sharing, lightweight
map- Transforms supplier outputfilter- Filters supplier output with predicatezip- Combines two suppliers- Factory methods: 
constant - Type conversions: 
into_box,into_arc,into_rc 
use prism3_function::{ArcReadonlySupplier, ReadonlySupplier};
struct Executor<E> {
    error_supplier: ArcReadonlySupplier<E>,
}
impl<E> Executor<E> {
    fn execute(&self) -> Result<(), E> {
        // Can call directly in &self method!
        Err(self.error_supplier.get())
    }
}use prism3_function::{ArcReadonlySupplier, ReadonlySupplier};
use std::thread;
let factory = ArcReadonlySupplier::new(|| String::from("Hello, World!"));
let handles: Vec<_> = (0..10)
    .map(|_| {
        let f = factory.clone();
        thread::spawn(move || f.get()) // Lock-free!
    })
    .collect();
for h in handles {
    assert_eq!(h.join().unwrap(), "Hello, World!");
}use prism3_function::{BoxReadonlySupplier, ReadonlySupplier};
#[derive(Clone)]
struct Config {
    timeout: u64,
}
let config_factory = BoxReadonlySupplier::new(|| Config { timeout: 30 });
assert_eq!(config_factory.get().timeout, 30);
assert_eq!(config_factory.get().timeout, 30);For stateless scenarios in multi-threaded environments:
ArcSupplier<T>: RequiresMutex, lock contention on everyget()callArcReadonlySupplier<T>: Lock-free, can callget()concurrently without contention
Benchmark results show ArcReadonlySupplier can be 10x faster than ArcSupplier in high-concurrency scenarios.
Transforms values from type T to type R by consuming input. Similar to Java's Function<T, R>.
transform(&self, input: T) -> R- Transforms input value to output value (consumes input)- Corresponds to 
Fn(T) -> Rclosure 
BoxTransformer<T, R>: Reusable, single ownership (Fn)ArcTransformer<T, R>: Thread-safe, cloneable (Arc)RcTransformer<T, R>: Single-threaded, cloneable (Rc)BoxTransformerOnce<T, R>: One-time use (FnOnce)
and_then- Composes transformers sequentially (f.and_then(g) = g(f(x)))compose- Composes transformers in reverse order (f.compose(g) = f(g(x)))when- Creates conditional transformer with predicate- Factory methods: 
identity,constant - Type conversions: 
into_box,into_arc,into_rc,into_fn - Extension trait 
FnTransformerOpsfor closures 
UnaryOperator<T>- Type alias forTransformer<T, T>
All composition methods (and_then, compose, when, or_else) accept their parameters by value, which means:
- Ownership is transferred: The parameter (transformer or predicate) is consumed and becomes unavailable after the operation
 - To preserve the original: You must explicitly 
clone()it first (only works forArcTransformerandRcTransformer) - BoxTransformer cannot be cloned: Once used in a composition, it's consumed and no longer available
 
use prism3_function::{BoxTransformer, Transformer};
// Chain transformers for data transformation
let parse_and_double = BoxTransformer::new(|s: String| s.parse::<i32>().ok())
    .and_then(|opt: Option<i32>| opt.unwrap_or(0))
    .and_then(|x: i32| x * 2);
assert_eq!(parse_and_double.apply("21".to_string()), 42);
assert_eq!(parse_and_double.apply("invalid".to_string()), 0);use prism3_function::{BoxTransformer, Transformer};
// Apply transformation only when predicate is true
let double_if_positive = BoxTransformer::new(|x: i32| x * 2)
    .when(|x: &i32| *x > 0);
assert_eq!(double_if_positive.apply(5), Some(10));
assert_eq!(double_if_positive.apply(-5), None);use prism3_function::{BoxTransformer, Transformer};
// Different transformations based on condition
let transform = BoxTransformer::new(|x: i32| format!("Positive: {}", x * 2))
    .when(|x: &i32| *x > 0)
    .or_else(|x: i32| format!("Non-positive: {}", x - 1));
assert_eq!(transform.apply(5), "Positive: 10");
assert_eq!(transform.apply(-5), "Non-positive: -6");Accepts two input parameters and performs operations without returning a result. Similar to Java's BiConsumer<T, U>.
accept(&mut self, first: &T, second: &U)- Performs an operation on two value references- Corresponds to 
FnMut(&T, &U)closure 
BoxBiConsumer<T, U>: Single ownershipArcBiConsumer<T, U>: Thread-safe, cloneableRcBiConsumer<T, U>: Single-threaded, cloneableBoxBiConsumerOnce<T, U>: One-time use
and_then- Chains bi-consumers sequentiallywhen- Conditional execution with bi-predicate- Type conversions: 
into_box,into_arc,into_rc - Extension trait 
FnBiConsumerOpsfor closures 
ReadonlyBiConsumer- For pure observation without modifying consumer state
All composition methods (and_then, when, or_else) accept their parameters by value, which means:
- Ownership is transferred: The parameter (bi-consumer or bi-predicate) is consumed and becomes unavailable after the operation
 - To preserve the original: You must explicitly 
clone()it first (only works forArcBiConsumerandRcBiConsumer) - BoxBiConsumer cannot be cloned: Once used in a composition, it's consumed and no longer available
 
use prism3_function::{BoxBiConsumer, BiConsumer};
// Create a bi-consumer for pair operations
let mut bi_consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
    println!("Sum: {}", x + y);
});
bi_consumer.accept_once(&10, &20);use prism3_function::{BoxBiConsumer, BiConsumer};
use std::sync::{Arc, Mutex};
let log = Arc::new(Mutex::new(Vec::new()));
let log1 = log.clone();
let log2 = log.clone();
// Chain multiple bi-consumers
let mut bi_consumer = BoxBiConsumer::new(move |x: &i32, y: &i32| {
    log1.lock().unwrap().push(format!("Sum: {}", x + y));
})
.and_then(move |x: &i32, y: &i32| {
    log2.lock().unwrap().push(format!("Product: {}", x * y));
});
bi_consumer.accept_once(&3, &4);
assert_eq!(log.lock().unwrap().len(), 2);
// log contains: ["Sum: 7", "Product: 12"]use prism3_function::{BoxBiConsumer, BiConsumer};
use std::sync::{Arc, Mutex};
let log = Arc::new(Mutex::new(Vec::new()));
let log_clone = log.clone();
// Only execute when both values are positive
let mut bi_consumer = BoxBiConsumer::new(move |x: &i32, y: &i32| {
    log_clone.lock().unwrap().push(format!("{} + {} = {}", x, y, x + y));
})
.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
bi_consumer.accept_once(&3, &4);   // Logged
bi_consumer.accept_once(&-1, &4);  // Not logged
assert_eq!(log.lock().unwrap().len(), 1);use prism3_function::{BoxBiConsumer, BiConsumer};
use std::sync::{Arc, Mutex};
let log = Arc::new(Mutex::new(Vec::new()));
let log1 = log.clone();
let log2 = log.clone();
// Different operations based on condition
let mut bi_consumer = BoxBiConsumer::new(move |x: &i32, y: &i32| {
    log1.lock().unwrap().push(format!("Both positive: {} + {} = {}", x, y, x + y));
})
.when(|x: &i32, y: &i32| *x > 0 && *y > 0)
.or_else(move |x: &i32, y: &i32| {
    log2.lock().unwrap().push(format!("Has negative: {} * {} = {}", x, y, x * y));
});
bi_consumer.accept_once(&3, &4);   // "Both positive: 3 + 4 = 7"
bi_consumer.accept_once(&-1, &4);  // "Has negative: -1 * 4 = -4"
assert_eq!(log.lock().unwrap().len(), 2);Tests whether two values satisfy a condition, returning bool. Similar to Java's BiPredicate<T, U>.
test(&self, first: &T, second: &U) -> bool- Tests if two values satisfy the predicate condition- Corresponds to 
Fn(&T, &U) -> boolclosure 
BoxBiPredicate<T, U>: Single ownership, non-cloneableArcBiPredicate<T, U>: Thread-safe, cloneableRcBiPredicate<T, U>: Single-threaded, cloneable
- Logical composition: 
and,or,not,xor,nand,nor - Type-preserving method chaining
 - Type conversions: 
into_box,into_arc,into_rc - Extension trait 
FnBiPredicateOpsfor closures 
All logical composition methods (and, or, xor, nand, nor) accept the other parameter by value, which means:
- Ownership is transferred: The 
otherbi-predicate is consumed and becomes unavailable after the operation - To preserve the original: You must explicitly 
clone()it first (only works forArcBiPredicateandRcBiPredicate) BoxBiPredicatecannot be cloned: Once used in a composition, it's consumed
use prism3_function::{ArcBiPredicate, RcBiPredicate, BoxBiPredicate, BiPredicate};
// ArcBiPredicate and RcBiPredicate can be cloned
let is_sum_positive = ArcBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
let first_larger = ArcBiPredicate::new(|x: &i32, y: &i32| x > y);
// Clone to preserve the original
let combined = is_sum_positive.and(first_larger.clone());
// first_larger is still usable because we cloned it
assert!(first_larger.test(&10, &5));
// BoxBiPredicate: Cannot be cloned, will be consumed
let box_pred = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
let combined_box = box_pred.and(|x: &i32, y: &i32| x > y);
// box_pred is no longer available hereuse prism3_function::{ArcBiPredicate, BiPredicate};
// Create bi-predicates with logical composition
let is_sum_positive = ArcBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
let first_larger = ArcBiPredicate::new(|x: &i32, y: &i32| x > y);
// Clone to preserve the original predicate
let combined = is_sum_positive.and(first_larger.clone());
assert!(combined.test(&10, &5));
assert!(!combined.test(&3, &8));
// first_larger is still usable
assert!(first_larger.test(&10, &5));Transforms two input values to produce a result value. Similar to Java's BiFunction<T, U, R>.
transform(&self, first: T, second: U) -> R- Transforms two input values to output value (consumes inputs)- Corresponds to 
Fn(T, U) -> Rclosure 
BoxBiTransformer<T, U, R>: Reusable, single ownership (Fn)ArcBiTransformer<T, U, R>: Thread-safe, cloneable (Arc)RcBiTransformer<T, U, R>: Single-threaded, cloneable (Rc)BoxBiTransformerOnce<T, U, R>: One-time use (FnOnce)
and_then- Composes bi-transformer with transformerwhen- Creates conditional bi-transformer with bi-predicate- Type conversions: 
into_box,into_arc,into_rc,into_fn - Extension trait 
FnBiTransformerOpsfor closures 
BinaryOperator<T>- Type alias forBiTransformer<T, T, T>
All composition methods (and_then, when, or_else) accept their parameters by value, which means:
- Ownership is transferred: The parameter (transformer or bi-predicate) is consumed and becomes unavailable after the operation
 - To preserve the original: You must explicitly 
clone()it first (only works forArcBiTransformerandRcBiTransformer) - BoxBiTransformer cannot be cloned: Once used in a composition, it's consumed and no longer available
 
use prism3_function::{BoxBiTransformer, BiTransformer};
// Create a bi-transformer for combining two values
let add = BoxBiTransformer::new(|x: i32, y: i32| x + y);
assert_eq!(add.apply(10, 20), 30);
// Chain with transformer for further processing
let add_and_double = BoxBiTransformer::new(|x: i32, y: i32| x + y)
    .and_then(|sum: i32| sum * 2);
assert_eq!(add_and_double.apply(10, 20), 60);
// Multiple chaining
let complex = BoxBiTransformer::new(|x: i32, y: i32| x + y)
    .and_then(|sum: i32| sum * 2)
    .and_then(|doubled: i32| format!("Result: {}", doubled));
assert_eq!(complex.apply(10, 20), "Result: 60");use prism3_function::{BoxBiTransformer, BiTransformer};
// Only transform when both values are positive
let add_if_positive = BoxBiTransformer::new(|x: i32, y: i32| x + y)
    .when(|x: &i32, y: &i32| *x > 0 && *y > 0);
assert_eq!(add_if_positive.apply(3, 4), Some(7));
assert_eq!(add_if_positive.apply(-1, 4), None);
assert_eq!(add_if_positive.apply(3, -4), None);use prism3_function::{BoxBiTransformer, BiTransformer};
// Different transformations based on condition
let transform = BoxBiTransformer::new(|x: i32, y: i32| format!("Sum: {}", x + y))
    .when(|x: &i32, y: &i32| *x > 0 && *y > 0)
    .or_else(|x: i32, y: i32| format!("Product: {}", x * y));
assert_eq!(transform.apply(3, 4), "Sum: 7");
assert_eq!(transform.apply(-1, 4), "Product: -4");
assert_eq!(transform.apply(3, -4), "Product: -12");Compares two values and returns an Ordering. Similar to Java's Comparator<T>.
compare(&self, a: &T, b: &T) -> Ordering- Compares two values and returns ordering- Corresponds to 
Fn(&T, &T) -> Orderingclosure 
BoxComparator<T>: Single ownershipArcComparator<T>: Thread-safe, cloneableRcComparator<T>: Single-threaded, cloneable
reversed- Reverses the comparison orderthen_comparing- Chains comparators (secondary sort key)- Type conversions: 
into_box,into_arc,into_rc - Extension trait 
FnComparatorOpsfor closures 
use prism3_function::{ArcComparator, Comparator};
use std::cmp::Ordering;
// Create a comparator
let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
// Reverse the order
let reversed = cmp.reversed();
assert_eq!(reversed.compare(&5, &3), Ordering::Less);Tests whether a state or condition holds without accepting input parameters. Similar to Java's BooleanSupplier but with Rust's ownership semantics.
test(&self) -> bool- Tests if a state or condition holds- Corresponds to 
Fn() -> boolclosure 
BoxTester: Single ownership, non-cloneableArcTester: Thread-safe shared ownership, cloneableRcTester: Single-threaded shared ownership, cloneable
- Logical composition: 
and,or,not - Type conversions: 
into_box,into_arc,into_rc - Extension trait 
FnTesterOpsfor closures 
- Uses 
&self: Tester is only responsible for "judgment", not "state management" - State management is caller's responsibility: Tester only reads state, does not modify state
 - Repeatable calls: The same Tester can call 
test()multiple times - No TesterOnce: Very limited use cases, directly using closures is better
 
All logical composition methods (and, or, not) accept the other parameter by value, which means:
- Ownership is transferred: The 
othertester is consumed and becomes unavailable after the operation - To preserve the original: You must explicitly 
clone()it first (only works forArcTesterandRcTester) BoxTestercannot be cloned: Once used in a composition, it's consumed
use prism3_function::{ArcTester, RcTester, BoxTester, Tester};
// ArcTester and RcTester can be cloned
let is_ready = ArcTester::new(|| system_ready());
let is_healthy = ArcTester::new(|| health_check());
// Clone to preserve the original
let combined = is_ready.and(is_healthy.clone());
// is_healthy is still usable because we cloned it
assert!(is_healthy.test());
// BoxTester: Cannot be cloned, will be consumed
let box_tester = BoxTester::new(|| check_condition());
let combined_box = box_tester.and(|| another_check());
// box_tester is no longer available hereuse prism3_function::{BoxTester, Tester};
use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
// State managed externally
let count = Arc::new(AtomicUsize::new(0));
let count_clone = Arc::clone(&count);
let tester = BoxTester::new(move || {
    count_clone.load(Ordering::Relaxed) <= 3
});
assert!(tester.test());  // true (0)
count.fetch_add(1, Ordering::Relaxed);
assert!(tester.test());  // true (1)
count.fetch_add(1, Ordering::Relaxed);
assert!(tester.test());  // true (2)
count.fetch_add(1, Ordering::Relaxed);
assert!(tester.test());  // true (3)
count.fetch_add(1, Ordering::Relaxed);
assert!(!tester.test()); // false (4)use prism3_function::{BoxTester, Tester};
use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
// 模拟系统状态 - 使用原子类型
let cpu_usage = Arc::new(AtomicUsize::new(45)); // 45%
let memory_usage = Arc::new(AtomicUsize::new(60)); // 60%
let disk_space = Arc::new(AtomicUsize::new(25)); // 25% free
let network_connected = Arc::new(AtomicBool::new(true));
let service_running = Arc::new(AtomicBool::new(true));
// 创建各种条件检查器
let cpu_ok = BoxTester::new({
    let cpu = Arc::clone(&cpu_usage);
    move || cpu.load(Ordering::Relaxed) < 80
});
let memory_ok = BoxTester::new({
    let memory = Arc::clone(&memory_usage);
    move || memory.load(Ordering::Relaxed) < 90
});
let disk_ok = BoxTester::new({
    let disk = Arc::clone(&disk_space);
    move || disk.load(Ordering::Relaxed) > 10
});
let network_ok = BoxTester::new({
    let net = Arc::clone(&network_connected);
    move || net.load(Ordering::Relaxed)
});
let service_ok = BoxTester::new({
    let svc = Arc::clone(&service_running);
    move || svc.load(Ordering::Relaxed)
});
// 组合条件:系统健康检查
let system_healthy = cpu_ok
    .and(memory_ok)
    .and(disk_ok)
    .and(network_ok)
    .and(service_ok);
// 组合条件:紧急状态检查(CPU或内存过高)
let emergency_state = BoxTester::new({
    let cpu = Arc::clone(&cpu_usage);
    move || cpu.load(Ordering::Relaxed) > 95
}).or(BoxTester::new({
    let memory = Arc::clone(&memory_usage);
    move || memory.load(Ordering::Relaxed) > 95
}));
// 组合条件:服务可用性检查(网络和服务都正常)
let service_available = network_ok.and(service_ok);
// 组合条件:资源充足检查(CPU、内存、磁盘都正常)
let resources_adequate = cpu_ok.and(memory_ok).and(disk_ok);
// 使用组合条件
assert!(system_healthy.test());
assert!(!emergency_state.test());
assert!(service_available.test());
assert!(resources_adequate.test());
// 复杂的逻辑组合:系统可以处理新请求的条件
let can_handle_requests = service_available
    .and(resources_adequate)
    .and(BoxTester::new({
        let cpu = Arc::clone(&cpu_usage);
        let memory = Arc::clone(&memory_usage);
        move || cpu.load(Ordering::Relaxed) <= 95 && memory.load(Ordering::Relaxed) <= 95
    }));
assert!(can_handle_requests.test());
// 测试状态变化
cpu_usage.store(50, Ordering::Relaxed);
assert!(can_handle_requests.test());
// 模拟CPU使用率过高
cpu_usage.store(98, Ordering::Relaxed);
assert!(!can_handle_requests.test());use prism3_function::{ArcTester, Tester};
use std::thread;
let shared = ArcTester::new(|| true);
let clone = shared.clone();
let handle = thread::spawn(move || {
    clone.test()
});
assert!(handle.join().unwrap());use prism3_function::{ArcTester, Tester};
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use std::time::{Duration, Instant};
fn wait_until(tester: &dyn Tester, timeout: Duration) -> bool {
    let start = Instant::now();
    while !tester.test() {
        if start.elapsed() > timeout {
            return false;
        }
        thread::sleep(Duration::from_millis(100));
    }
    true
}
// Usage
let ready = Arc::new(AtomicBool::new(false));
let ready_clone = Arc::clone(&ready);
let tester = ArcTester::new(move || {
    ready_clone.load(Ordering::Acquire)
});
// Another thread sets the flag
let ready_clone2 = Arc::clone(&ready);
thread::spawn(move || {
    thread::sleep(Duration::from_secs(2));
    ready_clone2.store(true, Ordering::Release);
});
// Wait for condition
if wait_until(&tester, Duration::from_secs(5)) {
    println!("Condition met!");
} else {
    println!("Timeout!");
}Add this to your Cargo.toml:
[dependencies]
prism3-function = "0.1.0"This crate adopts the Trait + Multiple Implementations pattern, providing:
- Unified Interface: Each functional type has a trait defining core behavior
 - Specialized Implementations: Multiple concrete types optimized for different scenarios
 - Type Preservation: Composition methods return the same concrete type
 - Ownership Flexibility: Choose between single ownership, thread-safe sharing, or single-threaded sharing
 - Ergonomic API: Natural method chaining without explicit cloning
 
| Type | Box (Single) | Arc (Thread-Safe) | Rc (Single-Thread) | 
|---|---|---|---|
| Predicate | BoxPredicate | ArcPredicate | RcPredicate | 
| Consumer | BoxConsumer | ArcConsumer | RcConsumer | 
| Mutator | BoxMutator | ArcMutator | RcMutator | 
| Supplier | BoxSupplier | ArcSupplier | RcSupplier | 
| Transformer | BoxTransformer | ArcTransformer | RcTransformer | 
| BiConsumer | BoxBiConsumer | ArcBiConsumer | RcBiConsumer | 
| BiPredicate | BoxBiPredicate | ArcBiPredicate | RcBiPredicate | 
| BiTransformer | BoxBiTransformer | ArcBiTransformer | RcBiTransformer | 
| Comparator | BoxComparator | ArcComparator | RcComparator | 
| Tester | BoxTester | ArcTester | RcTester | 
- Box: Single ownership, cannot be cloned, consumes self
 - Arc: Shared ownership, thread-safe, cloneable
 - Rc: Shared ownership, single-threaded, cloneable
 
- Predicate Design | 中文
 - Consumer Design | 中文
 - Mutator Design | 中文
 - Supplier Design | 中文
 - Transformer Design | 中文
 - Tester Design | - 中文
 
The examples/ directory contains comprehensive demonstrations:
predicate_demo.rs: Predicate usage patternsconsumer_demo.rs: Consumer usage patternsmutator_demo.rs: Mutator usage patternsmutator_once_conditional_demo.rs: Conditional mutator patternssupplier_demo.rs: Supplier usage patternstransformer_demo.rs: Transformer usage patterns- And more...
 
Run examples with:
cargo run --example predicate_demo
cargo run --example consumer_demo
cargo run --example mutator_demoLicensed under Apache License, Version 2.0.
Haixing Hu starfish.hu@gmail.com