prism3_function/consumer_once.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025.
4 * 3-Prism Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # ConsumerOnce Types
10//!
11//! Provides implementations of one-time consumer interfaces for executing one-time operations
12//! that accept a single input parameter but return no result.
13//!
14//! This module provides a unified `ConsumerOnce` trait and one concrete implementation:
15//!
16//! - **`BoxConsumerOnce<T>`**: Box-based single ownership implementation
17//!
18//! # Why No Arc/Rc Variants?
19//!
20//! Unlike `Consumer` and `ReadonlyConsumer`, this module does **not** provide `ArcConsumerOnce`
21//! or `RcConsumerOnce` implementations. This is a design decision based on the fact that
22//! `FnOnce` semantics are fundamentally incompatible with shared ownership. See design docs for details.
23//!
24//! # Design Philosophy
25//!
26//! ConsumerOnce uses `FnOnce(&T)` semantics for truly one-time consumption operations.
27//! Unlike Consumer, ConsumerOnce consumes itself on first call. Suitable for initialization
28//! callbacks, cleanup callbacks, and similar scenarios.
29//!
30//! # Author
31//!
32//! Hu Haixing
33
34use std::fmt;
35
36use crate::predicate::{BoxPredicate, Predicate};
37
38// ============================================================================
39// 1. ConsumerOnce Trait - Unified ConsumerOnce Interface
40// ============================================================================
41
42/// ConsumerOnce trait - Unified one-time consumer interface
43///
44/// Defines the core behavior of all one-time consumer types. Similar to consumers
45/// implementing `FnOnce(&T)`, executes operations that accept a value reference but
46/// return no result (only side effects), consuming itself in the process.
47///
48/// # Automatic Implementation
49///
50/// - All closures implementing `FnOnce(&T)`
51/// - `BoxConsumerOnce<T>`
52///
53/// # Features
54///
55/// - **Unified Interface**: All consumer types share the same `accept` method signature
56/// - **Automatic Implementation**: Closures automatically implement this trait with zero overhead
57/// - **Type Conversion**: Can be converted to BoxConsumerOnce
58/// - **Generic Programming**: Write functions that work with any one-time consumer type
59///
60/// # Examples
61///
62/// ```rust
63/// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
64/// use std::sync::{Arc, Mutex};
65///
66/// fn apply_consumer<C: ConsumerOnce<i32>>(consumer: C, value: &i32) {
67/// consumer.accept(value);
68/// }
69///
70/// let log = Arc::new(Mutex::new(Vec::new()));
71/// let l = log.clone();
72/// let box_con = BoxConsumerOnce::new(move |x: &i32| {
73/// l.lock().unwrap().push(*x);
74/// });
75/// apply_consumer(box_con, &5);
76/// assert_eq!(*log.lock().unwrap(), vec![5]);
77/// ```
78///
79/// # Author
80///
81/// Hu Haixing
82pub trait ConsumerOnce<T> {
83 /// Execute one-time consumption operation
84 ///
85 /// Executes an operation on the given reference. The operation typically reads
86 /// the input value or produces side effects, but does not modify the input
87 /// value itself. Consumes self.
88 ///
89 /// # Parameters
90 ///
91 /// * `value` - Reference to the value to be consumed
92 ///
93 /// # Examples
94 ///
95 /// ```rust
96 /// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
97 ///
98 /// let consumer = BoxConsumerOnce::new(|x: &i32| println!("{}", x));
99 /// consumer.accept(&5);
100 /// ```
101 fn accept(self, value: &T);
102
103 /// Convert to BoxConsumerOnce
104 ///
105 /// **⚠️ Consumes `self`**: The original consumer will be unavailable after calling this method.
106 ///
107 /// # Returns
108 ///
109 /// Returns the wrapped `BoxConsumerOnce<T>`
110 ///
111 /// # Examples
112 ///
113 /// ```rust
114 /// use prism3_function::ConsumerOnce;
115 /// use std::sync::{Arc, Mutex};
116 ///
117 /// let log = Arc::new(Mutex::new(Vec::new()));
118 /// let l = log.clone();
119 /// let closure = move |x: &i32| {
120 /// l.lock().unwrap().push(*x);
121 /// };
122 /// let box_consumer = closure.into_box();
123 /// box_consumer.accept(&5);
124 /// assert_eq!(*log.lock().unwrap(), vec![5]);
125 /// ```
126 fn into_box(self) -> BoxConsumerOnce<T>
127 where
128 Self: Sized + 'static,
129 T: 'static;
130
131 /// Convert to closure
132 ///
133 /// **⚠️ Consumes `self`**: The original consumer will be unavailable after calling this method.
134 ///
135 /// Converts a one-time consumer to a closure that can be used directly in places
136 /// where the standard library requires `FnOnce`.
137 ///
138 /// # Returns
139 ///
140 /// Returns a closure implementing `FnOnce(&T)`
141 fn into_fn(self) -> impl FnOnce(&T)
142 where
143 Self: Sized + 'static,
144 T: 'static;
145}
146
147// ============================================================================
148// 2. BoxConsumerOnce - Single Ownership Implementation
149// ============================================================================
150
151/// BoxConsumerOnce struct
152///
153/// One-time consumer implementation based on `Box<dyn FnOnce(&T)>` for single ownership scenarios.
154/// This is the simplest consumer type for truly one-time use.
155///
156/// # Features
157///
158/// - **Single Ownership**: Not cloneable, transfers ownership on use
159/// - **Zero Overhead**: No reference counting or lock overhead
160/// - **One-time Use**: Consumes self on first call
161/// - **Builder Pattern**: Method chaining naturally consumes `self`
162///
163/// # Use Cases
164///
165/// Choose `BoxConsumerOnce` when:
166/// - Consumer is truly used only once
167/// - Building pipelines where ownership flows naturally
168/// - Consumer captures values that should be consumed
169/// - Performance critical and cannot accept shared overhead
170///
171/// # Performance
172///
173/// `BoxConsumerOnce` has the best performance:
174/// - No reference counting overhead
175/// - No lock acquisition or runtime borrow checking
176/// - Direct function call through vtable
177/// - Minimal memory footprint (single pointer)
178///
179/// # Examples
180///
181/// ```rust
182/// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
183///
184/// let consumer = BoxConsumerOnce::new(|x: &i32| {
185/// println!("Value: {}", x);
186/// });
187/// consumer.accept(&5);
188/// ```
189///
190/// # Author
191///
192/// Hu Haixing
193pub struct BoxConsumerOnce<T> {
194 function: Box<dyn FnOnce(&T)>,
195 name: Option<String>,
196}
197
198impl<T> BoxConsumerOnce<T>
199where
200 T: 'static,
201{
202 /// Create a new BoxConsumerOnce
203 ///
204 /// # Type Parameters
205 ///
206 /// * `F` - Closure type
207 ///
208 /// # Parameters
209 ///
210 /// * `f` - Closure to be wrapped
211 ///
212 /// # Returns
213 ///
214 /// Returns a new `BoxConsumerOnce<T>` instance
215 ///
216 /// # Examples
217 ///
218 /// ```rust
219 /// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
220 /// use std::sync::{Arc, Mutex};
221 ///
222 /// let log = Arc::new(Mutex::new(Vec::new()));
223 /// let l = log.clone();
224 /// let consumer = BoxConsumerOnce::new(move |x: &i32| {
225 /// l.lock().unwrap().push(*x + 1);
226 /// });
227 /// consumer.accept(&5);
228 /// assert_eq!(*log.lock().unwrap(), vec![6]);
229 /// ```
230 pub fn new<F>(f: F) -> Self
231 where
232 F: FnOnce(&T) + 'static,
233 {
234 BoxConsumerOnce {
235 function: Box::new(f),
236 name: None,
237 }
238 }
239
240 /// Get the consumer's name
241 pub fn name(&self) -> Option<&str> {
242 self.name.as_deref()
243 }
244
245 /// Set the consumer's name
246 pub fn set_name(&mut self, name: impl Into<String>) {
247 self.name = Some(name.into());
248 }
249
250 /// Sequentially chain another one-time consumer
251 ///
252 /// Returns a new consumer that executes the current operation first, then the next operation. Consumes self.
253 ///
254 /// # Type Parameters
255 ///
256 /// * `C` - Type of the next consumer
257 ///
258 /// # Parameters
259 ///
260 /// * `next` - Consumer to execute after the current operation. **Note: This
261 /// parameter is passed by value and will transfer ownership.** Since
262 /// `BoxConsumerOnce` cannot be cloned, the parameter will be consumed.
263 /// Can be:
264 /// - A closure: `|x: &T|`
265 /// - A `BoxConsumerOnce<T>`
266 /// - Any type implementing `ConsumerOnce<T>`
267 ///
268 /// # Returns
269 ///
270 /// Returns a new combined `BoxConsumerOnce<T>`
271 ///
272 /// # Examples
273 ///
274 /// ```rust
275 /// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
276 /// use std::sync::{Arc, Mutex};
277 ///
278 /// let log = Arc::new(Mutex::new(Vec::new()));
279 /// let l1 = log.clone();
280 /// let l2 = log.clone();
281 /// let first = BoxConsumerOnce::new(move |x: &i32| {
282 /// l1.lock().unwrap().push(*x * 2);
283 /// });
284 /// let second = BoxConsumerOnce::new(move |x: &i32| {
285 /// l2.lock().unwrap().push(*x + 10);
286 /// });
287 ///
288 /// // Both first and second are moved and consumed
289 /// let chained = first.and_then(second);
290 /// chained.accept(&5);
291 /// assert_eq!(*log.lock().unwrap(), vec![10, 15]);
292 /// // first.accept(&3); // Would not compile - moved
293 /// // second.accept(&3); // Would not compile - moved
294 /// ```
295 pub fn and_then<C>(self, next: C) -> Self
296 where
297 C: ConsumerOnce<T> + 'static,
298 {
299 let first = self.function;
300 let second = next;
301 BoxConsumerOnce::new(move |t| {
302 first(t);
303 second.accept(t);
304 })
305 }
306
307 /// Create a no-op consumer
308 ///
309 /// # Returns
310 ///
311 /// Returns a no-op consumer
312 ///
313 /// # Examples
314 ///
315 /// ```rust
316 /// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
317 ///
318 /// let noop = BoxConsumerOnce::<i32>::noop();
319 /// noop.accept(&42);
320 /// // Value unchanged
321 /// ```
322 pub fn noop() -> Self {
323 BoxConsumerOnce::new(|_| {})
324 }
325
326 /// Creates a conditional consumer
327 ///
328 /// Returns a consumer that only executes when a predicate is satisfied.
329 ///
330 /// # Parameters
331 ///
332 /// * `predicate` - The condition to check, can be:
333 /// - Closure: `|x: &T| -> bool`
334 /// - Function pointer: `fn(&T) -> bool`
335 /// - `BoxPredicate<T>`
336 /// - Any type implementing `Predicate<T>`
337 ///
338 /// # Returns
339 ///
340 /// Returns `BoxConditionalConsumerOnce<T>`
341 ///
342 /// # Examples
343 ///
344 /// ```rust
345 /// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
346 /// use std::sync::{Arc, Mutex};
347 ///
348 /// let log = Arc::new(Mutex::new(Vec::new()));
349 /// let l = log.clone();
350 /// let consumer = BoxConsumerOnce::new(move |x: &i32| {
351 /// l.lock().unwrap().push(*x);
352 /// });
353 /// let conditional = consumer.when(|x: &i32| *x > 0);
354 ///
355 /// conditional.accept(&5);
356 /// assert_eq!(*log.lock().unwrap(), vec![5]);
357 /// ```
358 pub fn when<P>(self, predicate: P) -> BoxConditionalConsumerOnce<T>
359 where
360 P: Predicate<T> + 'static,
361 {
362 BoxConditionalConsumerOnce {
363 consumer: self,
364 predicate: predicate.into_box(),
365 }
366 }
367}
368
369impl<T> ConsumerOnce<T> for BoxConsumerOnce<T> {
370 fn accept(self, value: &T) {
371 (self.function)(value)
372 }
373
374 fn into_box(self) -> BoxConsumerOnce<T>
375 where
376 T: 'static,
377 {
378 self
379 }
380
381 fn into_fn(self) -> impl FnOnce(&T)
382 where
383 T: 'static,
384 {
385 self.function
386 }
387}
388
389impl<T> fmt::Debug for BoxConsumerOnce<T> {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 f.debug_struct("BoxConsumerOnce")
392 .field("name", &self.name)
393 .field("function", &"<function>")
394 .finish()
395 }
396}
397
398impl<T> fmt::Display for BoxConsumerOnce<T> {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400 match &self.name {
401 Some(name) => write!(f, "BoxConsumerOnce({})", name),
402 None => write!(f, "BoxConsumerOnce"),
403 }
404 }
405}
406
407// ============================================================================
408// 3. BoxConditionalConsumerOnce - Box-based Conditional Consumer
409// ============================================================================
410
411/// BoxConditionalConsumerOnce struct
412///
413/// A conditional one-time consumer that only executes when a predicate is satisfied.
414/// Uses `BoxConsumerOnce` and `BoxPredicate` for single ownership semantics.
415///
416/// This type is typically created by calling `BoxConsumerOnce::when()` and is
417/// designed to work with the `or_else()` method to create if-then-else logic.
418///
419/// # Features
420///
421/// - **Single Ownership**: Not cloneable, consumes `self` on use
422/// - **Conditional Execution**: Only consumes when predicate returns `true`
423/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
424/// - **Implements ConsumerOnce**: Can be used anywhere a `ConsumerOnce` is expected
425///
426/// # Examples
427///
428/// ## Basic Conditional Execution
429///
430/// ```rust
431/// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
432/// use std::sync::{Arc, Mutex};
433///
434/// let log = Arc::new(Mutex::new(Vec::new()));
435/// let l = log.clone();
436/// let consumer = BoxConsumerOnce::new(move |x: &i32| {
437/// l.lock().unwrap().push(*x);
438/// });
439/// let conditional = consumer.when(|x: &i32| *x > 0);
440///
441/// conditional.accept(&5);
442/// assert_eq!(*log.lock().unwrap(), vec![5]); // Executed
443/// ```
444///
445/// ## With or_else Branch
446///
447/// ```rust
448/// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
449/// use std::sync::{Arc, Mutex};
450///
451/// let log = Arc::new(Mutex::new(Vec::new()));
452/// let l1 = log.clone();
453/// let l2 = log.clone();
454/// let consumer = BoxConsumerOnce::new(move |x: &i32| {
455/// l1.lock().unwrap().push(*x);
456/// })
457/// .when(|x: &i32| *x > 0)
458/// .or_else(move |x: &i32| {
459/// l2.lock().unwrap().push(-*x);
460/// });
461///
462/// consumer.accept(&5);
463/// assert_eq!(*log.lock().unwrap(), vec![5]); // when branch executed
464/// ```
465///
466/// # Author
467///
468/// Hu Haixing
469pub struct BoxConditionalConsumerOnce<T> {
470 consumer: BoxConsumerOnce<T>,
471 predicate: BoxPredicate<T>,
472}
473
474impl<T> ConsumerOnce<T> for BoxConditionalConsumerOnce<T>
475where
476 T: 'static,
477{
478 fn accept(self, value: &T) {
479 if self.predicate.test(value) {
480 self.consumer.accept(value);
481 }
482 }
483
484 fn into_box(self) -> BoxConsumerOnce<T> {
485 let pred = self.predicate;
486 let consumer = self.consumer;
487 BoxConsumerOnce::new(move |t| {
488 if pred.test(t) {
489 consumer.accept(t);
490 }
491 })
492 }
493
494 fn into_fn(self) -> impl FnOnce(&T) {
495 let pred = self.predicate;
496 let consumer = self.consumer;
497 move |t: &T| {
498 if pred.test(t) {
499 consumer.accept(t);
500 }
501 }
502 }
503}
504
505impl<T> BoxConditionalConsumerOnce<T>
506where
507 T: 'static,
508{
509 /// Chains another consumer in sequence
510 ///
511 /// Combines the current conditional consumer with another consumer into a new
512 /// consumer. The current conditional consumer executes first, followed by the
513 /// next consumer.
514 ///
515 /// # Parameters
516 ///
517 /// * `next` - The next consumer to execute. **Note: This parameter is passed
518 /// by value and will transfer ownership.** Since `BoxConsumerOnce` cannot
519 /// be cloned, the parameter will be consumed. Can be:
520 /// - A closure: `|x: &T|`
521 /// - A `BoxConsumerOnce<T>`
522 /// - Any type implementing `ConsumerOnce<T>`
523 ///
524 /// # Returns
525 ///
526 /// Returns a new `BoxConsumerOnce<T>`
527 ///
528 /// # Examples
529 ///
530 /// ```rust
531 /// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
532 /// use std::sync::{Arc, Mutex};
533 ///
534 /// let log = Arc::new(Mutex::new(Vec::new()));
535 /// let l1 = log.clone();
536 /// let l2 = log.clone();
537 /// let cond1 = BoxConsumerOnce::new(move |x: &i32| {
538 /// l1.lock().unwrap().push(*x * 2);
539 /// }).when(|x: &i32| *x > 0);
540 /// let cond2 = BoxConsumerOnce::new(move |x: &i32| {
541 /// l2.lock().unwrap().push(*x + 100);
542 /// }).when(|x: &i32| *x > 10);
543 ///
544 /// // Both cond1 and cond2 are moved and consumed
545 /// let chained = cond1.and_then(cond2);
546 /// chained.accept(&6);
547 /// assert_eq!(*log.lock().unwrap(), vec![12, 106]); // First *2 = 12, then +100 = 106
548 /// // cond1.accept(&3); // Would not compile - moved
549 /// // cond2.accept(&3); // Would not compile - moved
550 /// ```
551 pub fn and_then<C>(self, next: C) -> BoxConsumerOnce<T>
552 where
553 C: ConsumerOnce<T> + 'static,
554 {
555 let first = self;
556 let second = next;
557 BoxConsumerOnce::new(move |t| {
558 first.accept(t);
559 second.accept(t);
560 })
561 }
562
563 /// Adds an else branch
564 ///
565 /// Executes the original consumer when the condition is satisfied, otherwise
566 /// executes else_consumer.
567 ///
568 /// # Parameters
569 ///
570 /// * `else_consumer` - The consumer for the else branch. **Note: This parameter
571 /// is passed by value and will transfer ownership.** Since `BoxConsumerOnce`
572 /// cannot be cloned, the parameter will be consumed. Can be:
573 /// - A closure: `|x: &T|`
574 /// - A `BoxConsumerOnce<T>`
575 /// - Any type implementing `ConsumerOnce<T>`
576 ///
577 /// # Returns
578 ///
579 /// Returns the composed `BoxConsumerOnce<T>`
580 ///
581 /// # Examples
582 ///
583 /// ## Using a closure (recommended)
584 ///
585 /// ```rust
586 /// use prism3_function::{ConsumerOnce, BoxConsumerOnce};
587 /// use std::sync::{Arc, Mutex};
588 ///
589 /// let log = Arc::new(Mutex::new(Vec::new()));
590 /// let l1 = log.clone();
591 /// let l2 = log.clone();
592 /// let consumer = BoxConsumerOnce::new(move |x: &i32| {
593 /// l1.lock().unwrap().push(*x);
594 /// })
595 /// .when(|x: &i32| *x > 0)
596 /// .or_else(move |x: &i32| {
597 /// l2.lock().unwrap().push(-*x);
598 /// });
599 ///
600 /// consumer.accept(&5);
601 /// assert_eq!(*log.lock().unwrap(), vec![5]); // Condition satisfied, execute first
602 /// ```
603 pub fn or_else<C>(self, else_consumer: C) -> BoxConsumerOnce<T>
604 where
605 C: ConsumerOnce<T> + 'static,
606 {
607 let pred = self.predicate;
608 let then_cons = self.consumer;
609 let else_cons = else_consumer;
610 BoxConsumerOnce::new(move |t| {
611 if pred.test(t) {
612 then_cons.accept(t);
613 } else {
614 else_cons.accept(t);
615 }
616 })
617 }
618}
619
620// ============================================================================
621// 4. Implement ConsumerOnce trait for closures
622// ============================================================================
623
624/// Implement ConsumerOnce for all FnOnce(&T)
625impl<T, F> ConsumerOnce<T> for F
626where
627 F: FnOnce(&T),
628{
629 fn accept(self, value: &T) {
630 self(value)
631 }
632
633 fn into_box(self) -> BoxConsumerOnce<T>
634 where
635 Self: Sized + 'static,
636 T: 'static,
637 {
638 BoxConsumerOnce::new(self)
639 }
640
641 fn into_fn(self) -> impl FnOnce(&T)
642 where
643 Self: Sized + 'static,
644 T: 'static,
645 {
646 self
647 }
648}
649
650// ============================================================================
651// 5. Extension methods for closures
652// ============================================================================
653
654/// Extension trait providing one-time consumer composition methods for closures
655///
656/// Provides `and_then` and other composition methods for all closures implementing `FnOnce(&T)`,
657/// allowing closures to chain methods directly without explicit wrapper types.
658///
659/// # Features
660///
661/// - **Natural Syntax**: Chain operations directly on closures
662/// - **Returns BoxConsumerOnce**: Composed results can continue chaining
663/// - **Zero Cost**: No overhead when composing closures
664/// - **Automatic Implementation**: All `FnOnce(&T)` closures automatically get these methods
665///
666/// # Examples
667///
668/// ```rust
669/// use prism3_function::{ConsumerOnce, FnConsumerOnceOps};
670/// use std::sync::{Arc, Mutex};
671///
672/// let log = Arc::new(Mutex::new(Vec::new()));
673/// let l1 = log.clone();
674/// let l2 = log.clone();
675/// let chained = (move |x: &i32| {
676/// l1.lock().unwrap().push(*x * 2);
677/// }).and_then(move |x: &i32| {
678/// l2.lock().unwrap().push(*x + 10);
679/// });
680/// chained.accept(&5);
681/// assert_eq!(*log.lock().unwrap(), vec![10, 15]);
682/// ```
683///
684/// # Author
685///
686/// Hu Haixing
687pub trait FnConsumerOnceOps<T>: FnOnce(&T) + Sized {
688 /// Sequentially chain another one-time consumer
689 ///
690 /// Returns a new consumer that executes the current operation first, then the next operation.
691 /// Consumes the current closure and returns `BoxConsumerOnce<T>`.
692 ///
693 /// # Type Parameters
694 ///
695 /// * `C` - Type of the next consumer
696 ///
697 /// # Parameters
698 ///
699 /// * `next` - Consumer to execute after the current operation. **Note: This
700 /// parameter is passed by value and will transfer ownership.** Since
701 /// `BoxConsumerOnce` cannot be cloned, the parameter will be consumed.
702 /// Can be:
703 /// - A closure: `|x: &T|`
704 /// - A `BoxConsumerOnce<T>`
705 /// - Any type implementing `ConsumerOnce<T>`
706 ///
707 /// # Returns
708 ///
709 /// Returns a combined `BoxConsumerOnce<T>`
710 ///
711 /// # Examples
712 ///
713 /// ```rust
714 /// use prism3_function::{ConsumerOnce, FnConsumerOnceOps};
715 /// use std::sync::{Arc, Mutex};
716 ///
717 /// let log = Arc::new(Mutex::new(Vec::new()));
718 /// let l1 = log.clone();
719 /// let l2 = log.clone();
720 /// let chained = (move |x: &i32| {
721 /// l1.lock().unwrap().push(*x * 2);
722 /// }).and_then(move |x: &i32| {
723 /// l2.lock().unwrap().push(*x + 10);
724 /// }).and_then(|x: &i32| println!("Result: {}", x));
725 ///
726 /// chained.accept(&5);
727 /// assert_eq!(*log.lock().unwrap(), vec![10, 15]);
728 /// ```
729 fn and_then<C>(self, next: C) -> BoxConsumerOnce<T>
730 where
731 Self: 'static,
732 C: ConsumerOnce<T> + 'static,
733 T: 'static,
734 {
735 let first = self;
736 let second = next;
737 BoxConsumerOnce::new(move |t| {
738 first(t);
739 second.accept(t);
740 })
741 }
742}
743
744/// Implement FnConsumerOnceOps for all closure types
745impl<T, F> FnConsumerOnceOps<T> for F where F: FnOnce(&T) {}