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!(" test(&5, &3): {} (always false)", combined_and_false.test(&5, &3));
112 println!(" test(&-3, &-5): {} (always false)", combined_and_false.test(&-3, &-5));
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!(" test(&5, &3): {} (always true)", combined_or_true.test(&5, &3));
119 println!(" test(&-3, &-5): {} (always true)", combined_or_true.test(&-3, &-5));
120
121 // Combining with always_false (OR)
122 let sum_positive = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
123 let combined_or_false = sum_positive.or(BoxBiPredicate::always_false());
124 println!("\nsum_positive OR always_false:");
125 println!(
126 " test(&5, &3): {} (equivalent to sum_positive)",
127 combined_or_false.test(&5, &3)
128 );
129 println!(
130 " test(&-3, &-5): {} (equivalent to sum_positive)",
131 combined_or_false.test(&-3, &-5)
132 );
133
134 println!("\n=== Practical scenarios: Default pass/reject filters ===\n");
135
136 // Scenario 1: Default pass-all filter
137 let pairs = vec![(1, 2), (3, 4), (5, 6)];
138 let pass_all = BoxBiPredicate::<i32, i32>::always_true();
139 let closure = pass_all.into_fn();
140 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
141 println!("Default pass all elements: {:?} -> {:?}", pairs, filtered);
142
143 // Scenario 2: Default reject-all filter
144 let pairs = vec![(1, 2), (3, 4), (5, 6)];
145 let reject_all = BoxBiPredicate::<i32, i32>::always_false();
146 let closure = reject_all.into_fn();
147 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
148 println!("Default reject all elements: {:?} -> {:?}", pairs, filtered);
149
150 // Scenario 3: Configurable filter
151 fn configurable_filter(enable_filter: bool) -> BoxBiPredicate<i32, i32> {
152 if enable_filter {
153 BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 5)
154 } else {
155 BoxBiPredicate::always_true()
156 }
157 }
158
159 let pairs = vec![(1, 2), (3, 4), (5, 6)];
160
161 let filter_enabled = configurable_filter(true);
162 let closure = filter_enabled.into_fn();
163 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
164 println!("\nFilter enabled: {:?} -> {:?}", pairs, filtered);
165
166 let filter_disabled = configurable_filter(false);
167 let closure = filter_disabled.into_fn();
168 let filtered: Vec<_> = pairs.iter().filter(|(x, y)| closure(x, y)).collect();
169 println!("Filter disabled: {:?} -> {:?}", pairs, filtered);
170}fn to_box(&self) -> BoxBiPredicate<T, U>
fn to_rc(&self) -> RcBiPredicate<T, U>
fn to_arc(&self) -> ArcBiPredicate<T, U>
fn to_fn(&self) -> impl Fn(&T, &U) -> bool
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".