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