pub trait BiPredicate<T, U> {
// Required method
fn test(&self, first: &T, second: &U) -> bool;
// Provided methods
fn into_box(self) -> BoxBiPredicate<T, U>
where Self: Sized + 'static,
T: 'static,
U: 'static { ... }
fn into_rc(self) -> RcBiPredicate<T, U>
where Self: Sized + 'static,
T: 'static,
U: 'static { ... }
fn into_arc(self) -> ArcBiPredicate<T, U>
where Self: Sized + Send + Sync + 'static,
T: Send + Sync + 'static,
U: Send + Sync + 'static { ... }
fn into_fn(self) -> impl Fn(&T, &U) -> bool
where Self: Sized + 'static,
T: 'static,
U: 'static { ... }
fn to_box(&self) -> BoxBiPredicate<T, U>
where Self: Sized + Clone + 'static,
T: 'static,
U: 'static { ... }
fn to_rc(&self) -> RcBiPredicate<T, U>
where Self: Sized + Clone + 'static,
T: 'static,
U: 'static { ... }
fn to_arc(&self) -> ArcBiPredicate<T, U>
where Self: Sized + Clone + Send + Sync + 'static,
T: Send + Sync + 'static,
U: Send + Sync + 'static { ... }
fn to_fn(&self) -> impl Fn(&T, &U) -> bool
where Self: Sized + Clone + 'static,
T: 'static,
U: 'static { ... }
}Expand description
A bi-predicate trait for testing whether two values satisfy a condition.
This trait represents a pure judgment operation - it tests whether two given values meet certain criteria without modifying either the values or the bi-predicate itself (from the user’s perspective). This semantic clarity distinguishes bi-predicates from consumers or transformers.
§Design Rationale
This is a minimal trait that only defines:
- The core
testmethod using&self(immutable borrow) - Type conversion methods (
into_box,into_rc,into_arc) - Closure conversion method (
into_fn)
Logical composition methods (and, or, not, xor, nand,
nor) are intentionally not part of the trait. Instead, they
are implemented on concrete types (BoxBiPredicate,
RcBiPredicate, ArcBiPredicate), allowing each implementation
to maintain its specific ownership characteristics:
BoxBiPredicate: Methods consumeself(single ownership)RcBiPredicate: Methods borrow&self(shared ownership)ArcBiPredicate: Methods borrow&self(thread-safe shared ownership)
§Why &self Instead of &mut self?
Bi-predicates use &self because:
- Semantic Clarity: A bi-predicate is a judgment, not a mutation
- Flexibility: Can be used in immutable contexts
- Simplicity: No need for
mutin user code - Interior Mutability: State (if needed) can be managed with
RefCell,Cell, orMutex
§Automatic Implementation for Closures
Any closure matching Fn(&T, &U) -> bool automatically implements
this trait, providing seamless integration with Rust’s closure
system.
§Examples
§Basic Usage
use prism3_function::bi_predicate::BiPredicate;
let is_sum_positive = |x: &i32, y: &i32| x + y > 0;
assert!(is_sum_positive.test(&5, &3));
assert!(!is_sum_positive.test(&-5, &-3));§Type Conversion
use prism3_function::bi_predicate::{BiPredicate,
BoxBiPredicate};
let closure = |x: &i32, y: &i32| x + y > 0;
let boxed: BoxBiPredicate<i32, i32> = closure.into_box();
assert!(boxed.test(&5, &3));§Stateful BiPredicate with Interior Mutability
use prism3_function::bi_predicate::{BiPredicate,
BoxBiPredicate};
use std::cell::Cell;
let count = Cell::new(0);
let counting_pred = BoxBiPredicate::new(move |x: &i32, y: &i32| {
count.set(count.get() + 1);
x + y > 0
});
// Note: No `mut` needed - interior mutability handles state
assert!(counting_pred.test(&5, &3));
assert!(!counting_pred.test(&-5, &-3));§Author
Haixing Hu
Required Methods§
Provided Methods§
Sourcefn into_box(self) -> BoxBiPredicate<T, U>where
Self: Sized + 'static,
T: 'static,
U: 'static,
fn into_box(self) -> BoxBiPredicate<T, U>where
Self: Sized + 'static,
T: 'static,
U: 'static,
Sourcefn into_rc(self) -> RcBiPredicate<T, U>where
Self: Sized + 'static,
T: 'static,
U: 'static,
fn into_rc(self) -> RcBiPredicate<T, U>where
Self: Sized + 'static,
T: 'static,
U: 'static,
Sourcefn into_arc(self) -> ArcBiPredicate<T, U>
fn into_arc(self) -> ArcBiPredicate<T, U>
Converts this bi-predicate into an ArcBiPredicate.
§Returns
An ArcBiPredicate wrapping this bi-predicate.
§Default Implementation
The default implementation wraps the bi-predicate in a
closure that calls test, providing automatic conversion
for custom types that only implement the core test
method. Note that this requires Send + Sync bounds for
thread-safe sharing.
Sourcefn into_fn(self) -> impl Fn(&T, &U) -> boolwhere
Self: Sized + 'static,
T: 'static,
U: 'static,
fn into_fn(self) -> impl Fn(&T, &U) -> boolwhere
Self: Sized + 'static,
T: 'static,
U: 'static,
Converts this bi-predicate into a closure that can be used directly with standard library methods.
This method consumes the bi-predicate and returns a closure
with signature Fn(&T, &U) -> bool. Since Fn is a subtrait
of FnMut, the returned closure can be used in any context
that requires either Fn(&T, &U) -> bool or
FnMut(&T, &U) -> bool.
§Returns
A closure implementing Fn(&T, &U) -> bool (also usable as
FnMut(&T, &U) -> bool).
§Default Implementation
The default implementation returns a closure that calls the
test method, providing automatic conversion for custom
types.
§Examples
§Using with Iterator Methods
use prism3_function::bi_predicate::{BiPredicate,
BoxBiPredicate};
let pred = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
let pairs = vec![(1, 2), (-1, 3), (5, -6)];
let mut closure = pred.into_fn();
let positives: Vec<_> = pairs.iter()
.filter(|(x, y)| closure(x, y))
.collect();
assert_eq!(positives, vec![&(1, 2), &(-1, 3)]);Examples found in repository?
11fn main() {
12 println!("=== BoxBiPredicate always_true/always_false Demo ===\n");
13
14 // BoxBiPredicate::always_true
15 let always_true: BoxBiPredicate<i32, i32> = BoxBiPredicate::always_true();
16 println!("BoxBiPredicate::always_true():");
17 println!(" test(&42, &10): {}", always_true.test(&42, &10));
18 println!(" test(&-1, &5): {}", always_true.test(&-1, &5));
19 println!(" test(&0, &0): {}", always_true.test(&0, &0));
20 println!(" name: {:?}", always_true.name());
21
22 // BoxBiPredicate::always_false
23 let always_false: BoxBiPredicate<i32, i32> = BoxBiPredicate::always_false();
24 println!("\nBoxBiPredicate::always_false():");
25 println!(" test(&42, &10): {}", always_false.test(&42, &10));
26 println!(" test(&-1, &5): {}", always_false.test(&-1, &5));
27 println!(" test(&0, &0): {}", always_false.test(&0, &0));
28 println!(" name: {:?}", always_false.name());
29
30 println!("\n=== RcBiPredicate always_true/always_false Demo ===\n");
31
32 // RcBiPredicate::always_true
33 let rc_always_true: RcBiPredicate<String, i32> = RcBiPredicate::always_true();
34 println!("RcBiPredicate::always_true():");
35 println!(
36 " test(&\"hello\", &5): {}",
37 rc_always_true.test(&"hello".to_string(), &5)
38 );
39 println!(
40 " test(&\"world\", &-3): {}",
41 rc_always_true.test(&"world".to_string(), &-3)
42 );
43 println!(" name: {:?}", rc_always_true.name());
44
45 // RcBiPredicate::always_false
46 let rc_always_false: RcBiPredicate<String, i32> = RcBiPredicate::always_false();
47 println!("\nRcBiPredicate::always_false():");
48 println!(
49 " test(&\"hello\", &5): {}",
50 rc_always_false.test(&"hello".to_string(), &5)
51 );
52 println!(
53 " test(&\"world\", &-3): {}",
54 rc_always_false.test(&"world".to_string(), &-3)
55 );
56 println!(" name: {:?}", rc_always_false.name());
57
58 // Can be cloned and reused
59 let rc_clone = rc_always_true.clone();
60 println!("\nAfter cloning, still usable:");
61 println!(
62 " Original: test(&\"test\", &1): {}",
63 rc_always_true.test(&"test".to_string(), &1)
64 );
65 println!(
66 " Clone: test(&\"test\", &2): {}",
67 rc_clone.test(&"test".to_string(), &2)
68 );
69
70 println!("\n=== ArcBiPredicate always_true/always_false Demo ===\n");
71
72 // ArcBiPredicate::always_true
73 let arc_always_true: ArcBiPredicate<i32, i32> = ArcBiPredicate::always_true();
74 println!("ArcBiPredicate::always_true():");
75 println!(" test(&100, &50): {}", arc_always_true.test(&100, &50));
76 println!(" test(&-100, &25): {}", arc_always_true.test(&-100, &25));
77 println!(" name: {:?}", arc_always_true.name());
78
79 // ArcBiPredicate::always_false
80 let arc_always_false: ArcBiPredicate<i32, i32> = ArcBiPredicate::always_false();
81 println!("\nArcBiPredicate::always_false():");
82 println!(" test(&100, &50): {}", arc_always_false.test(&100, &50));
83 println!(" test(&-100, &25): {}", arc_always_false.test(&-100, &25));
84 println!(" name: {:?}", arc_always_false.name());
85
86 println!("\n=== Combining with other bi-predicates ===\n");
87
88 // Combining with always_true (AND)
89 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
90 let combined_and_true = sum_positive.and(BoxBiPredicate::always_true());
91 println!("sum_positive AND always_true:");
92 println!(
93 " test(&5, &3): {} (equivalent to sum_positive)",
94 combined_and_true.test(&5, &3)
95 );
96 println!(
97 " test(&-3, &-5): {} (equivalent to sum_positive)",
98 combined_and_true.test(&-3, &-5)
99 );
100
101 // Combining with always_false (AND)
102 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
103 let combined_and_false = sum_positive.and(BoxBiPredicate::always_false());
104 println!("\nsum_positive AND always_false:");
105 println!(
106 " test(&5, &3): {} (always false)",
107 combined_and_false.test(&5, &3)
108 );
109 println!(
110 " test(&-3, &-5): {} (always false)",
111 combined_and_false.test(&-3, &-5)
112 );
113
114 // Combining with always_true (OR)
115 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
116 let combined_or_true = sum_positive.or(BoxBiPredicate::always_true());
117 println!("\nsum_positive OR always_true:");
118 println!(
119 " test(&5, &3): {} (always true)",
120 combined_or_true.test(&5, &3)
121 );
122 println!(
123 " test(&-3, &-5): {} (always true)",
124 combined_or_true.test(&-3, &-5)
125 );
126
127 // Combining with always_false (OR)
128 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
129 let combined_or_false = sum_positive.or(BoxBiPredicate::always_false());
130 println!("\nsum_positive OR always_false:");
131 println!(
132 " test(&5, &3): {} (equivalent to sum_positive)",
133 combined_or_false.test(&5, &3)
134 );
135 println!(
136 " test(&-3, &-5): {} (equivalent to sum_positive)",
137 combined_or_false.test(&-3, &-5)
138 );
139
140 println!("\n=== Practical scenarios: Default pass/reject filters ===\n");
141
142 // Scenario 1: Default pass-all filter
143 let pairs = vec![(1, 2), (3, 4), (5, 6)];
144 let pass_all = BoxBiPredicate::<i32, i32>::always_true();
145 let closure = pass_all.into_fn();
146 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
147 println!("Default pass all elements: {:?} -> {:?}", pairs, filtered);
148
149 // Scenario 2: Default reject-all filter
150 let pairs = vec![(1, 2), (3, 4), (5, 6)];
151 let reject_all = BoxBiPredicate::<i32, i32>::always_false();
152 let closure = reject_all.into_fn();
153 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
154 println!("Default reject all elements: {:?} -> {:?}", pairs, filtered);
155
156 // Scenario 3: Configurable filter
157 fn configurable_filter(enable_filter: bool) -> BoxBiPredicate<i32, i32> {
158 if enable_filter {
159 BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 5)
160 } else {
161 BoxBiPredicate::always_true()
162 }
163 }
164
165 let pairs = vec![(1, 2), (3, 4), (5, 6)];
166
167 let filter_enabled = configurable_filter(true);
168 let closure = filter_enabled.into_fn();
169 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
170 println!("\nFilter enabled: {:?} -> {:?}", pairs, filtered);
171
172 let filter_disabled = configurable_filter(false);
173 let closure = filter_disabled.into_fn();
174 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
175 println!("Filter disabled: {:?} -> {:?}", pairs, filtered);
176}