pub struct ArcBiConsumer<T, U> { /* private fields */ }Expand description
ArcBiConsumer struct
A non-mutating bi-consumer implementation based on
Arc<dyn Fn(&T, &U) + Send + Sync> for thread-safe shared ownership
scenarios. The wrapper does not need Mutex because it only invokes a
shared Fn.
§Features
- Shared Ownership: Cloneable via
Arc, multiple owners allowed - Thread-Safe: Implements
Send + Sync, safe for concurrent use - Lock-free Wrapper: No Mutex protection needed by the wrapper
- Non-Consuming API:
and_thenborrows&self, original remains usable
§Use Cases
Choose ArcBiConsumer when:
- Need to share non-mutating bi-consumer across multiple threads
- Pure observation operations like logging, monitoring, notifications
- Need high-concurrency reads without lock overhead
§Performance Advantages
Compared to ArcStatefulBiConsumer, ArcBiConsumer has no Mutex locking
overhead, resulting in better performance in high-concurrency observation
scenarios.
§Examples
use qubit_function::{BiConsumer, ArcBiConsumer};
let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
println!("Sum: {}", x + y);
});
let clone = consumer.clone();
consumer.accept(&5, &3);
clone.accept(&10, &20);Implementations§
Source§impl<T, U> ArcBiConsumer<T, U>
impl<T, U> ArcBiConsumer<T, U>
Sourcepub fn new<F>(f: F) -> Self
pub fn new<F>(f: F) -> Self
Creates a new bi-consumer.
Wraps the provided closure in the appropriate smart pointer type for this bi-consumer implementation.
Examples found in repository?
30fn main() {
31 println!("=== BiConsumer Observation Demo ===\n");
32
33 // 1. BoxBiConsumer - Single ownership
34 println!("1. BoxBiConsumer - Single ownership:");
35 let box_consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
36 println!(" Values: x={}, y={}, sum={}", x, y, x + y);
37 });
38 box_consumer.accept(&10, &5);
39 println!();
40
41 // 2. Method chaining with BoxBiConsumer
42 println!("2. BoxBiConsumer with method chaining:");
43 let chained = BoxBiConsumer::new(|x: &i32, y: &i32| {
44 println!(" First operation: x={}, y={}", x, y);
45 })
46 .and_then(|x: &i32, y: &i32| {
47 println!(" Second operation: sum={}", x + y);
48 })
49 .and_then(|x: &i32, y: &i32| {
50 println!(" Third operation: product={}", x * y);
51 });
52 chained.accept(&5, &3);
53 println!();
54
55 // 3. ArcBiConsumer - Thread-safe shared ownership
56 println!("3. ArcBiConsumer - Thread-safe shared ownership:");
57 let counter = Arc::new(AtomicUsize::new(0));
58 let c = counter.clone();
59 let arc_consumer = ArcBiConsumer::new(move |x: &i32, y: &i32| {
60 c.fetch_add(1, Ordering::SeqCst);
61 println!(" Thread {:?}: sum={}", thread::current().id(), x + y);
62 });
63
64 let consumer1 = arc_consumer.clone();
65 let consumer2 = arc_consumer.clone();
66
67 let handle1 = thread::spawn(move || {
68 consumer1.accept(&10, &5);
69 });
70
71 let handle2 = thread::spawn(move || {
72 consumer2.accept(&20, &8);
73 });
74
75 handle1.join().expect("thread should not panic");
76 handle2.join().expect("thread should not panic");
77 println!(" Total calls: {}\n", counter.load(Ordering::SeqCst));
78
79 // 4. RcBiConsumer - Single-threaded shared ownership
80 println!("4. RcBiConsumer - Single-threaded shared ownership:");
81 let counter = Rc::new(std::cell::Cell::new(0));
82 let c = counter.clone();
83 let rc_consumer = RcBiConsumer::new(move |x: &i32, y: &i32| {
84 c.set(c.get() + 1);
85 println!(" Call {}: sum={}", c.get(), x + y);
86 });
87
88 let clone1 = rc_consumer.clone();
89 let clone2 = rc_consumer.clone();
90
91 clone1.accept(&5, &3);
92 clone2.accept(&7, &2);
93 println!(" Total calls: {}\n", counter.get());
94
95 // 5. Working with closures directly
96 println!("5. Working with closures directly:");
97 let closure = |x: &i32, y: &i32| {
98 println!(" x={}, y={}, product={}", x, y, x * y);
99 };
100 closure.accept(&10, &20);
101 println!();
102
103 // 6. Pure observation - logging
104 println!("6. Pure observation - logging:");
105 let logger = BoxBiConsumer::new(|x: &i32, y: &i32| {
106 println!(" [LOG] Processing pair: ({}, {})", x, y);
107 });
108 logger.accept(&5, &3);
109 logger.accept(&10, &7);
110 println!();
111
112 // 7. Chaining observations
113 println!("7. Chaining observations:");
114 let log_input = BoxBiConsumer::new(|x: &i32, y: &i32| {
115 println!(" [INPUT] x={}, y={}", x, y);
116 });
117 let log_sum = BoxBiConsumer::new(|x: &i32, y: &i32| {
118 println!(" [SUM] {}", x + y);
119 });
120 let log_product = BoxBiConsumer::new(|x: &i32, y: &i32| {
121 println!(" [PRODUCT] {}", x * y);
122 });
123
124 let chained = log_input.and_then(log_sum).and_then(log_product);
125 chained.accept(&5, &3);
126 println!();
127
128 // 8. ArcBiConsumer - Reusability
129 println!("8. ArcBiConsumer - Reusability:");
130 let first = ArcBiConsumer::new(|x: &i32, y: &i32| {
131 println!(" First: x={}, y={}", x, y);
132 });
133 let second = ArcBiConsumer::new(|x: &i32, y: &i32| {
134 println!(" Second: sum={}", x + y);
135 });
136
137 // Both first and second can be reused after chaining
138 let chained1 = first.and_then(second.clone());
139 let chained2 = first.and_then(second.clone());
140
141 println!(" Using chained1:");
142 chained1.accept(&5, &3);
143
144 println!(" Using chained2:");
145 chained2.accept(&10, &2);
146 println!();
147
148 // 9. Name support
149 println!("9. Name support:");
150 let mut named_consumer = BoxBiConsumer::<i32, i32>::noop();
151 println!(" Initial name: {:?}", named_consumer.name());
152
153 named_consumer.set_name("sum_logger");
154 println!(" After setting name: {:?}", named_consumer.name());
155 println!(" Display: {}\n", named_consumer);
156
157 // 10. No-op consumer
158 println!("10. No-op consumer:");
159 let noop = BoxBiConsumer::<i32, i32>::noop();
160 noop.accept(&42, &10);
161 println!(" No-op completed (no output expected)\n");
162
163 println!("=== Demo Complete ===");
164}More examples
32fn main() {
33 println!("=== BiConsumer Demo ===\n");
34
35 // 1. BoxBiConsumer - Single ownership
36 println!("1. BoxBiConsumer - Single ownership:");
37 let log = Arc::new(Mutex::new(Vec::new()));
38 let l = log.clone();
39 let box_consumer = BoxBiConsumer::new(move |x: &i32, y: &i32| {
40 println!(" Processing: x={}, y={}", x, y);
41 l.lock()
42 .expect("mutex should not be poisoned")
43 .push(*x + *y);
44 });
45 box_consumer.accept(&10, &5);
46 println!(
47 " Result log: {:?}\n",
48 *log.lock().expect("mutex should not be poisoned")
49 );
50
51 // 2. Method chaining with BoxBiConsumer
52 println!("2. BoxBiConsumer with method chaining:");
53 let log = Arc::new(Mutex::new(Vec::new()));
54 let l1 = log.clone();
55 let l2 = log.clone();
56 let chained = BoxBiConsumer::new(move |x: &i32, y: &i32| {
57 l1.lock()
58 .expect("mutex should not be poisoned")
59 .push(*x + *y);
60 println!(" After first operation: sum = {}", x + y);
61 })
62 .and_then(move |x: &i32, y: &i32| {
63 l2.lock()
64 .expect("mutex should not be poisoned")
65 .push(*x * *y);
66 println!(" After second operation: product = {}", x * y);
67 });
68 chained.accept(&5, &3);
69 println!(
70 " Final log: {:?}\n",
71 *log.lock().expect("mutex should not be poisoned")
72 );
73
74 // 3. ArcBiConsumer - Thread-safe shared ownership
75 println!("3. ArcBiConsumer - Thread-safe shared ownership:");
76 let log = Arc::new(Mutex::new(Vec::new()));
77 let l = log.clone();
78 let arc_consumer = ArcBiConsumer::new(move |x: &i32, y: &i32| {
79 l.lock()
80 .expect("mutex should not be poisoned")
81 .push(*x + *y);
82 println!(" Thread {:?}: sum = {}", thread::current().id(), x + y);
83 });
84
85 let consumer1 = arc_consumer.clone();
86 let consumer2 = arc_consumer.clone();
87
88 let handle1 = thread::spawn(move || {
89 let c = consumer1;
90 c.accept(&10, &5);
91 });
92
93 let handle2 = thread::spawn(move || {
94 let c = consumer2;
95 c.accept(&20, &8);
96 });
97
98 handle1.join().expect("thread should not panic");
99 handle2.join().expect("thread should not panic");
100 println!(
101 " Final log: {:?}\n",
102 *log.lock().expect("mutex should not be poisoned")
103 );
104
105 // 4. RcBiConsumer - Single-threaded shared ownership
106 println!("4. RcBiConsumer - Single-threaded shared ownership:");
107 let log = Rc::new(RefCell::new(Vec::new()));
108 let l = log.clone();
109 let rc_consumer = RcBiConsumer::new(move |x: &i32, y: &i32| {
110 l.borrow_mut().push(*x + *y);
111 });
112
113 let clone1 = rc_consumer.clone();
114 let clone2 = rc_consumer.clone();
115
116 clone1.accept(&5, &3);
117 println!(" After first use: {:?}", *log.borrow());
118
119 clone2.accept(&7, &2);
120 println!(" After second use: {:?}\n", *log.borrow());
121
122 // 5. Working with closures directly
123 println!("5. Working with closures directly:");
124 let log = Arc::new(Mutex::new(Vec::new()));
125 let l = log.clone();
126 let closure = move |x: &i32, y: &i32| {
127 let sum = *x + *y;
128 l.lock().expect("mutex should not be poisoned").push(sum);
129 };
130 closure.accept(&10, &20);
131 println!(
132 " After closure: {:?}\n",
133 *log.lock().expect("mutex should not be poisoned")
134 );
135
136 // 6. Conditional BiConsumer
137 println!("6. Conditional BiConsumer:");
138 let log = Arc::new(Mutex::new(Vec::new()));
139 let l = log.clone();
140 let mut conditional = BoxStatefulBiConsumer::new(move |x: &i32, y: &i32| {
141 l.lock()
142 .expect("mutex should not be poisoned")
143 .push(*x + *y);
144 })
145 .when(|x: &i32, y: &i32| *x > 0 && *y > 0);
146
147 conditional.accept(&5, &3);
148 println!(
149 " Positive values: {:?}",
150 *log.lock().expect("mutex should not be poisoned")
151 );
152
153 conditional.accept(&-5, &3);
154 println!(
155 " Negative value (unchanged): {:?}\n",
156 *log.lock().expect("mutex should not be poisoned")
157 );
158
159 // 7. Conditional branch BiConsumer
160 println!("7. Conditional branch BiConsumer:");
161 let log = Arc::new(Mutex::new(Vec::new()));
162 let l1 = log.clone();
163 let l2 = log.clone();
164 let mut branch = BoxStatefulBiConsumer::new(move |x: &i32, _y: &i32| {
165 l1.lock().expect("mutex should not be poisoned").push(*x);
166 })
167 .when(|x: &i32, y: &i32| *x > *y)
168 .or_else(move |_x: &i32, y: &i32| {
169 l2.lock().expect("mutex should not be poisoned").push(*y);
170 });
171
172 branch.accept(&15, &10);
173 println!(
174 " When x > y: {:?}",
175 *log.lock().expect("mutex should not be poisoned")
176 );
177
178 branch.accept(&5, &10);
179 println!(
180 " When x <= y: {:?}\n",
181 *log.lock().expect("mutex should not be poisoned")
182 );
183
184 // 8. Accumulating statistics
185 println!("8. Accumulating statistics:");
186 let count = Arc::new(Mutex::new(0));
187 let sum = Arc::new(Mutex::new(0));
188 let c = count.clone();
189 let s = sum.clone();
190 let stats_consumer = BoxBiConsumer::new(move |x: &i32, y: &i32| {
191 *c.lock().expect("mutex should not be poisoned") += 1;
192 *s.lock().expect("mutex should not be poisoned") += x + y;
193 });
194
195 stats_consumer.accept(&5, &3);
196 stats_consumer.accept(&10, &2);
197 stats_consumer.accept(&7, &8);
198
199 println!(
200 " Count: {}",
201 *count.lock().expect("mutex should not be poisoned")
202 );
203 println!(
204 " Sum: {}\n",
205 *sum.lock().expect("mutex should not be poisoned")
206 );
207
208 // 9. Name support
209 println!("9. Name support:");
210 let mut named_consumer = BoxBiConsumer::<i32, i32>::noop();
211 println!(" Initial name: {:?}", named_consumer.name());
212
213 named_consumer.set_name("sum_calculator");
214 println!(" After setting name: {:?}", named_consumer.name());
215 println!(" Display: {}\n", named_consumer);
216
217 println!("=== Demo Complete ===");
218}Sourcepub fn new_with_name<F>(name: &str, f: F) -> Self
pub fn new_with_name<F>(name: &str, f: F) -> Self
Creates a new named bi-consumer.
Wraps the provided closure and assigns it a name, which is useful for debugging and logging purposes.
Sourcepub fn new_with_optional_name<F>(f: F, name: Option<String>) -> Self
pub fn new_with_optional_name<F>(f: F, name: Option<String>) -> Self
Creates a new named bi-consumer with an optional name.
Wraps the provided closure and assigns it an optional name.
Sourcepub fn clear_name(&mut self)
pub fn clear_name(&mut self)
Clears the name of this bi-consumer.
Sourcepub fn noop() -> Self
pub fn noop() -> Self
Creates a no-operation bi-consumer.
Creates a bi-consumer that does nothing when called. Useful for default values or placeholder implementations.
§Returns
Returns a new bi-consumer instance that performs no operation.
Sourcepub fn when<P>(&self, predicate: P) -> ArcConditionalBiConsumer<T, U>
pub fn when<P>(&self, predicate: P) -> ArcConditionalBiConsumer<T, U>
Creates a conditional bi-consumer
Wraps this bi-consumer with a bi-predicate condition, creating a new conditional bi-consumer that will only execute the original bi-consumer when the predicate evaluates to true.
§Parameters
predicate- The condition that must be satisfied for the bi-consumer to execute
§Returns
Returns a conditional bi-consumer that executes this bi-consumer only when the predicate is satisfied
§Examples
use qubit_function::{ArcBiConsumer, BiConsumer};
let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| println!("{}", x + y));
let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
conditional.accept(&5, &3); // prints: 8
conditional.accept(&-5, &3); // prints nothingSourcepub fn and_then<C>(&self, after: C) -> ArcBiConsumer<T, U>
pub fn and_then<C>(&self, after: C) -> ArcBiConsumer<T, U>
Chains another bi-consumer in sequence
Combines this bi-consumer with another bi-consumer into a new
bi-consumer that executes both bi-consumers in sequence. The returned
bi-consumer first executes this bi-consumer, then unconditionally
executes the after bi-consumer.
§Parameters
after- The bi-consumer to execute after this one (always executed)
§Returns
Returns a new bi-consumer that executes both bi-consumers in sequence
§Examples
use qubit_function::{ArcBiConsumer, BiConsumer};
let consumer1 = ArcBiConsumer::new(|x: &i32, y: &i32| print!("first: {}", x + y));
let consumer2 = ArcBiConsumer::new(|x: &i32, y: &i32| println!(" second: {}", x * y));
let chained = consumer1.and_then(consumer2);
chained.accept(&5, &3); // prints: first: 8 second: 15Examples found in repository?
30fn main() {
31 println!("=== BiConsumer Observation Demo ===\n");
32
33 // 1. BoxBiConsumer - Single ownership
34 println!("1. BoxBiConsumer - Single ownership:");
35 let box_consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
36 println!(" Values: x={}, y={}, sum={}", x, y, x + y);
37 });
38 box_consumer.accept(&10, &5);
39 println!();
40
41 // 2. Method chaining with BoxBiConsumer
42 println!("2. BoxBiConsumer with method chaining:");
43 let chained = BoxBiConsumer::new(|x: &i32, y: &i32| {
44 println!(" First operation: x={}, y={}", x, y);
45 })
46 .and_then(|x: &i32, y: &i32| {
47 println!(" Second operation: sum={}", x + y);
48 })
49 .and_then(|x: &i32, y: &i32| {
50 println!(" Third operation: product={}", x * y);
51 });
52 chained.accept(&5, &3);
53 println!();
54
55 // 3. ArcBiConsumer - Thread-safe shared ownership
56 println!("3. ArcBiConsumer - Thread-safe shared ownership:");
57 let counter = Arc::new(AtomicUsize::new(0));
58 let c = counter.clone();
59 let arc_consumer = ArcBiConsumer::new(move |x: &i32, y: &i32| {
60 c.fetch_add(1, Ordering::SeqCst);
61 println!(" Thread {:?}: sum={}", thread::current().id(), x + y);
62 });
63
64 let consumer1 = arc_consumer.clone();
65 let consumer2 = arc_consumer.clone();
66
67 let handle1 = thread::spawn(move || {
68 consumer1.accept(&10, &5);
69 });
70
71 let handle2 = thread::spawn(move || {
72 consumer2.accept(&20, &8);
73 });
74
75 handle1.join().expect("thread should not panic");
76 handle2.join().expect("thread should not panic");
77 println!(" Total calls: {}\n", counter.load(Ordering::SeqCst));
78
79 // 4. RcBiConsumer - Single-threaded shared ownership
80 println!("4. RcBiConsumer - Single-threaded shared ownership:");
81 let counter = Rc::new(std::cell::Cell::new(0));
82 let c = counter.clone();
83 let rc_consumer = RcBiConsumer::new(move |x: &i32, y: &i32| {
84 c.set(c.get() + 1);
85 println!(" Call {}: sum={}", c.get(), x + y);
86 });
87
88 let clone1 = rc_consumer.clone();
89 let clone2 = rc_consumer.clone();
90
91 clone1.accept(&5, &3);
92 clone2.accept(&7, &2);
93 println!(" Total calls: {}\n", counter.get());
94
95 // 5. Working with closures directly
96 println!("5. Working with closures directly:");
97 let closure = |x: &i32, y: &i32| {
98 println!(" x={}, y={}, product={}", x, y, x * y);
99 };
100 closure.accept(&10, &20);
101 println!();
102
103 // 6. Pure observation - logging
104 println!("6. Pure observation - logging:");
105 let logger = BoxBiConsumer::new(|x: &i32, y: &i32| {
106 println!(" [LOG] Processing pair: ({}, {})", x, y);
107 });
108 logger.accept(&5, &3);
109 logger.accept(&10, &7);
110 println!();
111
112 // 7. Chaining observations
113 println!("7. Chaining observations:");
114 let log_input = BoxBiConsumer::new(|x: &i32, y: &i32| {
115 println!(" [INPUT] x={}, y={}", x, y);
116 });
117 let log_sum = BoxBiConsumer::new(|x: &i32, y: &i32| {
118 println!(" [SUM] {}", x + y);
119 });
120 let log_product = BoxBiConsumer::new(|x: &i32, y: &i32| {
121 println!(" [PRODUCT] {}", x * y);
122 });
123
124 let chained = log_input.and_then(log_sum).and_then(log_product);
125 chained.accept(&5, &3);
126 println!();
127
128 // 8. ArcBiConsumer - Reusability
129 println!("8. ArcBiConsumer - Reusability:");
130 let first = ArcBiConsumer::new(|x: &i32, y: &i32| {
131 println!(" First: x={}, y={}", x, y);
132 });
133 let second = ArcBiConsumer::new(|x: &i32, y: &i32| {
134 println!(" Second: sum={}", x + y);
135 });
136
137 // Both first and second can be reused after chaining
138 let chained1 = first.and_then(second.clone());
139 let chained2 = first.and_then(second.clone());
140
141 println!(" Using chained1:");
142 chained1.accept(&5, &3);
143
144 println!(" Using chained2:");
145 chained2.accept(&10, &2);
146 println!();
147
148 // 9. Name support
149 println!("9. Name support:");
150 let mut named_consumer = BoxBiConsumer::<i32, i32>::noop();
151 println!(" Initial name: {:?}", named_consumer.name());
152
153 named_consumer.set_name("sum_logger");
154 println!(" After setting name: {:?}", named_consumer.name());
155 println!(" Display: {}\n", named_consumer);
156
157 // 10. No-op consumer
158 println!("10. No-op consumer:");
159 let noop = BoxBiConsumer::<i32, i32>::noop();
160 noop.accept(&42, &10);
161 println!(" No-op completed (no output expected)\n");
162
163 println!("=== Demo Complete ===");
164}