empty_box/
lib.rs

1//! # `EmptyBox`, a way to safely move values in and out of `Box`s without
2//! reallocations
3//!
4//! `EmptyBox` is similar to a statically checked `Box<Option<T>>`:
5//!
6//! ```
7//! use empty_box::EmptyBox;
8//!
9//! // A box with a string!
10//! let boxed = Box::new("Hello!".to_string());
11//!
12//! // Oh no, we don't like that string.
13//! let (string, empty) = EmptyBox::take(boxed);
14//!
15//! // Let's make an objectively superior string, and put it into the original
16//! // box.
17//! let superior = "Objectively superior string!".to_string();
18//!
19//! // Now we have our superior string in the box!
20//! let boxed = empty.put(superior); 
21//!
22//! assert_eq!("Hello!", string);
23//! assert_eq!("Objectively superior string!", &*boxed);
24//! ```
25//!
26//! Creating an `EmptyBox` from a `Box` and then putting a `T` back into the
27//! `EmptyBox` will avoid allocating a new `Box`, instead reusing whatever old
28//! `Box` the `T` was `EmptyBox::take`n from.
29
30use std::mem;
31use std::ptr;
32
33
34/// An "emptied" `Box`. Constructed via `EmptyBox::take()`, an `EmptyBox<T>` is
35/// a `Box` from which the contents have been moved. This allows for reuse of the
36/// `Box` via `EmptyBox::put()`, which moves the contents back in, turning the
37/// `EmptyBox` back into a `Box<T>`.
38pub struct EmptyBox<T> {
39    ptr: *mut T,
40}
41
42
43impl<T> Drop for EmptyBox<T> {
44    fn drop(&mut self) {
45        let boxed = unsafe { Box::from_raw(self.ptr) };
46        let inner = *boxed;
47        mem::forget(inner);
48    }
49}
50
51
52impl<T> EmptyBox<T> {
53    /// Move the value out of the `Box`, creating a `T` and an `EmptyBox` which
54    /// preserves the original `Box`'s allocation.
55    pub fn take(bx: Box<T>) -> (T, EmptyBox<T>) {
56        let ptr = Box::into_raw(bx);
57        let t = unsafe { ptr::read(ptr) };
58        (t, EmptyBox { ptr })
59    }
60
61
62    /// Restore a value to an `EmptyBox`, creating a new `Box` and reusing the
63    /// allocation of whatever `Box` was destroyed to create the `EmptyBox`.
64    pub fn put(self, t: T) -> Box<T> {
65        let EmptyBox { ptr } = self;
66
67        // `Drop` will be run otherwise - this is because even though we've
68        // clearly moved out of `self`, it persists since the only field we
69        // matched from it is `Copy`.
70        mem::forget(self); 
71
72        unsafe {
73            ptr::write(ptr, t);
74            Box::from_raw(ptr)
75        }
76    }
77}
78
79
80#[cfg(test)]
81mod test {
82    use std::cell::Cell;
83
84    use super::*;
85
86
87    #[derive(Clone)]
88    pub struct DropCounter<'a>(&'a Cell<usize>);
89
90    impl<'a> Drop for DropCounter<'a> {
91        fn drop(&mut self) {
92            let prev = self.0.get();
93            self.0.set(prev + 1);
94        }
95    }
96
97
98    #[test]
99    fn drop_counter() {
100        let counter = Cell::new(0);
101
102        mem::drop(DropCounter(&counter));
103        mem::drop(DropCounter(&counter));
104
105        let dc = DropCounter(&counter);
106
107        assert_eq!(counter.get(), 2);
108
109        mem::drop(dc);
110
111        assert_eq!(counter.get(), 3);
112    }
113
114
115    #[test]
116    fn no_drop() {
117        let counter = Cell::new(0);
118
119        let dc = {
120            let boxed = Box::new(DropCounter(&counter));
121            EmptyBox::take(boxed).0
122        };
123
124        assert_eq!(counter.get(), 0);
125
126        mem::drop(dc);
127    }
128
129
130    #[test]
131    fn two_drop() {
132        let counter = Cell::new(0);
133
134        let boxed = Box::new(DropCounter(&counter));
135        let (dc, empty) = EmptyBox::take(boxed);
136
137        mem::drop(dc);
138
139        mem::drop(empty.put(DropCounter(&counter)));
140
141        assert_eq!(counter.get(), 2);
142    }
143}