flat_drop/
lib.rs

1//! In this crate, we define the [FlatDrop] type.
2//! `FlatDrop<K>` behaves just like a `K`, but with a custom `Drop` implementation
3//! that avoids blowing up the stack when dropping large objects.
4//! Instead of recursively dropping subobjects, we perform a depth-first search
5//! and iteratively drop subobjects.
6//!
7//! To use this crate, you can replace recursive [Box]es and [Arc]s in your types
8//! with `FlatDrop<Box<T>>` or `FlatDrop<Arc<T>>`. You'll need to implement the
9//! [Recursive] trait for your type, which performs one step of the iterative
10//! dropping procedure.
11//!
12//! This crate uses `unsafe` internally, but the external API is safe.
13//!
14//! # Example
15//!
16//! ```
17//! use flat_drop::{FlatDrop, Recursive};
18//!
19//! /// Peano natural numbers.
20//! enum Natural {
21//!     Zero,
22//!     Succ(FlatDrop<Box<Natural>>),
23//! }
24//!
25//! impl Recursive for Natural {
26//!     type Container = Box<Natural>;
27//!
28//!     fn destruct(self) -> impl Iterator<Item = Self::Container> {
29//!         match self {
30//!             Natural::Zero => None,
31//!             Natural::Succ(pred) => Some(pred.into_inner()),
32//!         }
33//!         .into_iter()
34//!     }
35//! }
36//!
37//! impl Natural {
38//!     pub fn from_usize(value: usize) -> Self {
39//!         (0..value).fold(Self::Zero, |nat, _| {
40//!             Self::Succ(FlatDrop::new(Box::new(nat)))
41//!         })
42//!     }
43//! }
44//!
45//! // Create a new thread with a 4kb stack and allocate a number far bigger than 4 * 1024.
46//! const STACK_SIZE: usize = 4 * 1024;
47//!
48//! fn task() {
49//!     let nat = Natural::from_usize(STACK_SIZE * 100);
50//!     drop(std::hint::black_box(nat));
51//! }
52//!
53//! std::thread::Builder::new()
54//!     .stack_size(STACK_SIZE)
55//!     .spawn(task)
56//!     .unwrap()
57//!     .join()
58//!     .unwrap();
59//! ```
60
61use std::{
62    fmt::Display,
63    mem::ManuallyDrop,
64    ops::{Deref, DerefMut},
65    rc::Rc,
66    sync::Arc,
67};
68
69/// The [Recursive::destruct] function decomposes an object into some component parts.
70/// Usually, [Recursive::Container] is something like `Box<Self>` or `Arc<Self>`.
71pub trait Recursive {
72    type Container;
73
74    fn destruct(self) -> impl Iterator<Item = Self::Container>;
75}
76
77/// A trait for a smart pointer that contains (at most) a single value.
78pub trait IntoOptionInner {
79    type Inner;
80
81    /// A (potentially) fallible operation to convert the container into its internal value.
82    /// This should never drop any data.
83    ///
84    /// If `Self == Box`, this will always return `Some(*self)`.
85    /// If `Self == Arc`, this is `Arc::into_inner`.
86    fn into_option_inner(self) -> Option<Self::Inner>;
87}
88
89/// If `K` is a container of a recursive type, such as `Box<T>` where `T: Recursive`,
90/// `FlatDrop<K>` behaves just like `K`, but with a custom `Drop` implementation.
91/// In this implementation, we gather the recursive parts of the object iteratively
92/// and drop them without recursion, avoiding stack overflows when dropping
93/// large recursive objects.
94///
95/// # Safety
96///
97/// We keep the invariant that the inner object is always initialised, but will
98/// be dropped (exactly once) in the `drop` implementation.
99#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
100#[repr(transparent)]
101pub struct FlatDrop<K>(ManuallyDrop<K>)
102where
103    K: IntoOptionInner,
104    K::Inner: Recursive<Container = K>;
105
106impl<K> Drop for FlatDrop<K>
107where
108    K: IntoOptionInner,
109    K::Inner: Recursive<Container = K>,
110{
111    fn drop(&mut self) {
112        // Move out of the inner `ManuallyDrop`.
113        // Safety: the inner value has not yet been dropped, and will not be used again.
114        let value = unsafe { ManuallyDrop::take(&mut self.0) };
115
116        // Construct a sequence of containers to drop.
117        let mut to_drop = vec![value];
118
119        // Iteratively decompose each container from this list.
120        // This avoids creating excessive stack frames when destroying large objects.
121        while let Some(container) = to_drop.pop() {
122            if let Some(value) = container.into_option_inner() {
123                to_drop.extend(value.destruct());
124            }
125        }
126
127        // The drop glue will be a no-op since the field is `ManuallyDrop`.
128    }
129}
130
131// Now that we've defined the core parts of the library, we'll make some API.
132
133impl<T> IntoOptionInner for Box<T> {
134    type Inner = T;
135
136    fn into_option_inner(self) -> Option<Self::Inner> {
137        Some(*self)
138    }
139}
140
141impl<T> IntoOptionInner for Rc<T> {
142    type Inner = T;
143
144    fn into_option_inner(self) -> Option<Self::Inner> {
145        Rc::into_inner(self)
146    }
147}
148
149impl<T> IntoOptionInner for Arc<T> {
150    type Inner = T;
151
152    fn into_option_inner(self) -> Option<Self::Inner> {
153        Arc::into_inner(self)
154    }
155}
156
157impl<K> FlatDrop<K>
158where
159    K: IntoOptionInner,
160    K::Inner: Recursive<Container = K>,
161{
162    pub const fn new(container: K) -> Self {
163        Self(ManuallyDrop::new(container))
164    }
165
166    pub fn into_inner(mut self) -> K {
167        // Safety: This value is always initialised.
168        // Once we take it, we need to be careful to not call `drop` on `self`.
169        let value = unsafe { ManuallyDrop::take(&mut self.0) };
170        // This doesn't leak, because `self` is contained purely on the stack.
171        std::mem::forget(self);
172        value
173    }
174}
175
176impl<K, T> AsRef<T> for FlatDrop<K>
177where
178    T: ?Sized,
179    K: IntoOptionInner,
180    K::Inner: Recursive<Container = K>,
181    K: AsRef<T>,
182{
183    fn as_ref(&self) -> &T {
184        (**self).as_ref()
185    }
186}
187
188impl<K, T> AsMut<T> for FlatDrop<K>
189where
190    T: ?Sized,
191    K: IntoOptionInner,
192    K::Inner: Recursive<Container = K>,
193    K: AsMut<T>,
194{
195    fn as_mut(&mut self) -> &mut T {
196        (**self).as_mut()
197    }
198}
199
200impl<K> Deref for FlatDrop<K>
201where
202    K: IntoOptionInner,
203    K::Inner: Recursive<Container = K>,
204{
205    type Target = K;
206
207    fn deref(&self) -> &K {
208        self.0.deref()
209    }
210}
211
212impl<K> DerefMut for FlatDrop<K>
213where
214    K: IntoOptionInner,
215    K::Inner: Recursive<Container = K>,
216{
217    fn deref_mut(&mut self) -> &mut K {
218        self.0.deref_mut()
219    }
220}
221
222impl<K> Display for FlatDrop<K>
223where
224    K: IntoOptionInner + Display,
225    K::Inner: Recursive<Container = K>,
226{
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        <K as Display>::fmt(self, f)
229    }
230}
231
232impl<K> From<K> for FlatDrop<K>
233where
234    K: IntoOptionInner,
235    K::Inner: Recursive<Container = K>,
236{
237    fn from(value: K) -> Self {
238        Self::new(value)
239    }
240}
241
242impl<T> FlatDrop<Box<T>>
243where
244    T: Recursive<Container = Box<T>>,
245{
246    pub fn new_boxed(value: T) -> Self {
247        Self::new(Box::new(value))
248    }
249}
250
251impl<T> FlatDrop<Rc<T>>
252where
253    T: Recursive<Container = Rc<T>>,
254{
255    pub fn new_rc(value: T) -> Self {
256        Self::new(Rc::new(value))
257    }
258}
259
260impl<T> FlatDrop<Arc<T>>
261where
262    T: Recursive<Container = Arc<T>>,
263{
264    pub fn new_arc(value: T) -> Self {
265        Self::new(Arc::new(value))
266    }
267}
268
269#[cfg(feature = "serde")]
270impl<K> serde::Serialize for FlatDrop<K>
271where
272    K: IntoOptionInner,
273    K::Inner: Recursive<Container = K>,
274    K: serde::Serialize,
275{
276    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
277    where
278        S: serde::Serializer,
279    {
280        <K as serde::Serialize>::serialize(self, serializer)
281    }
282}
283
284#[cfg(feature = "serde")]
285impl<'de, K> serde::Deserialize<'de> for FlatDrop<K>
286where
287    K: IntoOptionInner,
288    K::Inner: Recursive<Container = K>,
289    K: serde::Deserialize<'de>,
290{
291    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292    where
293        D: serde::Deserializer<'de>,
294    {
295        <K as serde::Deserialize>::deserialize(deserializer).map(Self::new)
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use crate::{FlatDrop, Recursive};
302
303    /// Peano natural numbers.
304    enum Natural {
305        Zero,
306        Succ(FlatDrop<Box<Natural>>),
307    }
308
309    impl Recursive for Natural {
310        type Container = Box<Natural>;
311
312        fn destruct(self) -> impl Iterator<Item = Self::Container> {
313            match self {
314                Natural::Zero => None,
315                Natural::Succ(pred) => Some(pred.into_inner()),
316            }
317            .into_iter()
318        }
319    }
320
321    impl Natural {
322        pub fn from_usize(value: usize) -> Self {
323            (0..value).fold(Self::Zero, |nat, _| {
324                Self::Succ(FlatDrop::new(Box::new(nat)))
325            })
326        }
327    }
328
329    #[test]
330    fn test_large_natural() {
331        // Create a new thread with a 4kb stack and allocate a number far bigger than 4 * 1024.
332        const STACK_SIZE: usize = 4 * 1024;
333
334        fn task() {
335            let nat = Natural::from_usize(STACK_SIZE * 100);
336            println!("Dropping...");
337            drop(std::hint::black_box(nat));
338            println!("Dropped.");
339        }
340
341        std::thread::Builder::new()
342            .stack_size(STACK_SIZE)
343            .spawn(task)
344            .unwrap()
345            .join()
346            .unwrap();
347    }
348}