almost_enough/
boxed.rs

1//! Boxed dynamic dispatch for Stop.
2//!
3//! This module provides [`BoxedStop`], a heap-allocated wrapper that enables
4//! dynamic dispatch without monomorphization bloat.
5//!
6//! # When to Use
7//!
8//! Generic functions like `fn process(stop: impl Stop)` are monomorphized
9//! for each concrete type, increasing binary size. `BoxedStop` provides a
10//! single concrete type for dynamic dispatch:
11//!
12//! ```rust
13//! use almost_enough::{BoxedStop, Stop};
14//!
15//! // Monomorphized for each Stop type - increases binary size
16//! fn process_generic(stop: impl Stop) {
17//!     // ...
18//! }
19//!
20//! // Single implementation - no monomorphization bloat
21//! fn process_boxed(stop: BoxedStop) {
22//!     // ...
23//! }
24//! ```
25//!
26//! # Alternatives
27//!
28//! For borrowed dynamic dispatch with zero allocation, use `&dyn Stop`:
29//!
30//! ```rust
31//! use almost_enough::{StopSource, Stop};
32//!
33//! fn process(stop: &dyn Stop) {
34//!     if stop.should_stop() {
35//!         return;
36//!     }
37//!     // ...
38//! }
39//!
40//! let source = StopSource::new();
41//! process(&source);
42//! ```
43
44use alloc::boxed::Box;
45
46use crate::{Stop, StopReason};
47
48/// A heap-allocated [`Stop`] implementation.
49///
50/// This type provides dynamic dispatch for `Stop`, avoiding monomorphization
51/// bloat when you don't need the performance of generics.
52///
53/// # Example
54///
55/// ```rust
56/// use almost_enough::{BoxedStop, StopSource, Stopper, Never, Stop};
57///
58/// fn process(stop: BoxedStop) {
59///     for i in 0..1000 {
60///         if i % 100 == 0 && stop.should_stop() {
61///             return;
62///         }
63///         // process...
64///     }
65/// }
66///
67/// // Works with any Stop implementation
68/// process(BoxedStop::new(Never));
69/// process(BoxedStop::new(StopSource::new()));
70/// process(BoxedStop::new(Stopper::new()));
71/// ```
72pub struct BoxedStop(Box<dyn Stop + Send + Sync>);
73
74impl BoxedStop {
75    /// Create a new boxed stop from any [`Stop`] implementation.
76    #[inline]
77    pub fn new<T: Stop + 'static>(stop: T) -> Self {
78        Self(Box::new(stop))
79    }
80}
81
82impl Stop for BoxedStop {
83    #[inline]
84    fn check(&self) -> Result<(), StopReason> {
85        self.0.check()
86    }
87
88    #[inline]
89    fn should_stop(&self) -> bool {
90        self.0.should_stop()
91    }
92}
93
94impl core::fmt::Debug for BoxedStop {
95    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96        f.debug_tuple("BoxedStop").finish()
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use crate::{Never, StopSource, Stopper};
104
105    #[test]
106    fn boxed_stop_from_never() {
107        let stop = BoxedStop::new(Never);
108        assert!(!stop.should_stop());
109        assert!(stop.check().is_ok());
110    }
111
112    #[test]
113    fn boxed_stop_from_stopper() {
114        let stopper = Stopper::new();
115        let stop = BoxedStop::new(stopper.clone());
116
117        assert!(!stop.should_stop());
118
119        stopper.cancel();
120
121        assert!(stop.should_stop());
122        assert_eq!(stop.check(), Err(StopReason::Cancelled));
123    }
124
125    #[test]
126    fn boxed_stop_is_send_sync() {
127        fn assert_send_sync<T: Send + Sync>() {}
128        assert_send_sync::<BoxedStop>();
129    }
130
131    #[test]
132    fn boxed_stop_debug() {
133        let stop = BoxedStop::new(Never);
134        let debug = alloc::format!("{:?}", stop);
135        assert!(debug.contains("BoxedStop"));
136    }
137
138    #[test]
139    fn boxed_stop_avoids_monomorphization() {
140        // This function has a single concrete implementation
141        fn process(stop: BoxedStop) -> bool {
142            stop.should_stop()
143        }
144
145        // All these use the same process function
146        assert!(!process(BoxedStop::new(Never)));
147        assert!(!process(BoxedStop::new(StopSource::new())));
148        assert!(!process(BoxedStop::new(Stopper::new())));
149    }
150}