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 { ... }
fn into_rc(self) -> RcBiPredicate<T, U>
where Self: Sized + 'static { ... }
fn into_arc(self) -> ArcBiPredicate<T, U>
where Self: Sized + Send + Sync + 'static { ... }
fn into_fn(self) -> impl Fn(&T, &U) -> bool
where Self: Sized + 'static { ... }
fn to_box(&self) -> BoxBiPredicate<T, U>
where Self: Sized + Clone + 'static { ... }
fn to_rc(&self) -> RcBiPredicate<T, U>
where Self: Sized + Clone + 'static { ... }
fn to_arc(&self) -> ArcBiPredicate<T, U>
where Self: Sized + Clone + Send + Sync + 'static { ... }
fn to_fn(&self) -> impl Fn(&T, &U) -> bool
where Self: Sized + Clone + '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 qubit_function::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 qubit_function::{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 qubit_function::{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,
fn into_box(self) -> BoxBiPredicate<T, U>where
Self: Sized + 'static,
Sourcefn into_rc(self) -> RcBiPredicate<T, U>where
Self: Sized + 'static,
fn into_rc(self) -> RcBiPredicate<T, U>where
Self: Sized + '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,
fn into_fn(self) -> impl Fn(&T, &U) -> boolwhere
Self: Sized + '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 qubit_function::{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?
16fn main() {
17 println!("=== BoxBiPredicate always_true/always_false Demo ===\n");
18
19 // BoxBiPredicate::always_true
20 let always_true: BoxBiPredicate<i32, i32> = BoxBiPredicate::always_true();
21 println!("BoxBiPredicate::always_true():");
22 println!(" test(&42, &10): {}", always_true.test(&42, &10));
23 println!(" test(&-1, &5): {}", always_true.test(&-1, &5));
24 println!(" test(&0, &0): {}", always_true.test(&0, &0));
25 println!(" name: {:?}", always_true.name());
26
27 // BoxBiPredicate::always_false
28 let always_false: BoxBiPredicate<i32, i32> = BoxBiPredicate::always_false();
29 println!("\nBoxBiPredicate::always_false():");
30 println!(" test(&42, &10): {}", always_false.test(&42, &10));
31 println!(" test(&-1, &5): {}", always_false.test(&-1, &5));
32 println!(" test(&0, &0): {}", always_false.test(&0, &0));
33 println!(" name: {:?}", always_false.name());
34
35 println!("\n=== RcBiPredicate always_true/always_false Demo ===\n");
36
37 // RcBiPredicate::always_true
38 let rc_always_true: RcBiPredicate<String, i32> = RcBiPredicate::always_true();
39 println!("RcBiPredicate::always_true():");
40 println!(
41 " test(&\"hello\", &5): {}",
42 rc_always_true.test(&"hello".to_string(), &5)
43 );
44 println!(
45 " test(&\"world\", &-3): {}",
46 rc_always_true.test(&"world".to_string(), &-3)
47 );
48 println!(" name: {:?}", rc_always_true.name());
49
50 // RcBiPredicate::always_false
51 let rc_always_false: RcBiPredicate<String, i32> = RcBiPredicate::always_false();
52 println!("\nRcBiPredicate::always_false():");
53 println!(
54 " test(&\"hello\", &5): {}",
55 rc_always_false.test(&"hello".to_string(), &5)
56 );
57 println!(
58 " test(&\"world\", &-3): {}",
59 rc_always_false.test(&"world".to_string(), &-3)
60 );
61 println!(" name: {:?}", rc_always_false.name());
62
63 // Can be cloned and reused
64 let rc_clone = rc_always_true.clone();
65 println!("\nAfter cloning, still usable:");
66 println!(
67 " Original: test(&\"test\", &1): {}",
68 rc_always_true.test(&"test".to_string(), &1)
69 );
70 println!(
71 " Clone: test(&\"test\", &2): {}",
72 rc_clone.test(&"test".to_string(), &2)
73 );
74
75 println!("\n=== ArcBiPredicate always_true/always_false Demo ===\n");
76
77 // ArcBiPredicate::always_true
78 let arc_always_true: ArcBiPredicate<i32, i32> = ArcBiPredicate::always_true();
79 println!("ArcBiPredicate::always_true():");
80 println!(" test(&100, &50): {}", arc_always_true.test(&100, &50));
81 println!(" test(&-100, &25): {}", arc_always_true.test(&-100, &25));
82 println!(" name: {:?}", arc_always_true.name());
83
84 // ArcBiPredicate::always_false
85 let arc_always_false: ArcBiPredicate<i32, i32> = ArcBiPredicate::always_false();
86 println!("\nArcBiPredicate::always_false():");
87 println!(" test(&100, &50): {}", arc_always_false.test(&100, &50));
88 println!(" test(&-100, &25): {}", arc_always_false.test(&-100, &25));
89 println!(" name: {:?}", arc_always_false.name());
90
91 println!("\n=== Combining with other bi-predicates ===\n");
92
93 // Combining with always_true (AND)
94 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
95 let combined_and_true = sum_positive.and(BoxBiPredicate::always_true());
96 println!("sum_positive AND always_true:");
97 println!(
98 " test(&5, &3): {} (equivalent to sum_positive)",
99 combined_and_true.test(&5, &3)
100 );
101 println!(
102 " test(&-3, &-5): {} (equivalent to sum_positive)",
103 combined_and_true.test(&-3, &-5)
104 );
105
106 // Combining with always_false (AND)
107 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
108 let combined_and_false = sum_positive.and(BoxBiPredicate::always_false());
109 println!("\nsum_positive AND always_false:");
110 println!(
111 " test(&5, &3): {} (always false)",
112 combined_and_false.test(&5, &3)
113 );
114 println!(
115 " test(&-3, &-5): {} (always false)",
116 combined_and_false.test(&-3, &-5)
117 );
118
119 // Combining with always_true (OR)
120 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
121 let combined_or_true = sum_positive.or(BoxBiPredicate::always_true());
122 println!("\nsum_positive OR always_true:");
123 println!(
124 " test(&5, &3): {} (always true)",
125 combined_or_true.test(&5, &3)
126 );
127 println!(
128 " test(&-3, &-5): {} (always true)",
129 combined_or_true.test(&-3, &-5)
130 );
131
132 // Combining with always_false (OR)
133 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
134 let combined_or_false = sum_positive.or(BoxBiPredicate::always_false());
135 println!("\nsum_positive OR always_false:");
136 println!(
137 " test(&5, &3): {} (equivalent to sum_positive)",
138 combined_or_false.test(&5, &3)
139 );
140 println!(
141 " test(&-3, &-5): {} (equivalent to sum_positive)",
142 combined_or_false.test(&-3, &-5)
143 );
144
145 println!("\n=== Practical scenarios: Default pass/reject filters ===\n");
146
147 // Scenario 1: Default pass-all filter
148 let pairs = vec![(1, 2), (3, 4), (5, 6)];
149 let pass_all = BoxBiPredicate::<i32, i32>::always_true();
150 let closure = pass_all.into_fn();
151 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
152 println!("Default pass all elements: {:?} -> {:?}", pairs, filtered);
153
154 // Scenario 2: Default reject-all filter
155 let pairs = vec![(1, 2), (3, 4), (5, 6)];
156 let reject_all = BoxBiPredicate::<i32, i32>::always_false();
157 let closure = reject_all.into_fn();
158 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
159 println!("Default reject all elements: {:?} -> {:?}", pairs, filtered);
160
161 // Scenario 3: Configurable filter
162 fn configurable_filter(enable_filter: bool) -> BoxBiPredicate<i32, i32> {
163 if enable_filter {
164 BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 5)
165 } else {
166 BoxBiPredicate::always_true()
167 }
168 }
169
170 let pairs = vec![(1, 2), (3, 4), (5, 6)];
171
172 let filter_enabled = configurable_filter(true);
173 let closure = filter_enabled.into_fn();
174 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
175 println!("\nFilter enabled: {:?} -> {:?}", pairs, filtered);
176
177 let filter_disabled = configurable_filter(false);
178 let closure = filter_disabled.into_fn();
179 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
180 println!("Filter disabled: {:?} -> {:?}", pairs, filtered);
181}