1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! # `EmptyBox`, a way to safely move values in and out of `Box`s without
//! reallocations
//!
//! `EmptyBox` is similar to a statically checked `Box<Option<T>>`:
//!
//! ```
//! use empty_box::EmptyBox;
//!
//! // A box with a string!
//! let boxed = Box::new("Hello!".to_string());
//!
//! // Oh no, we don't like that string.
//! let (string, empty) = EmptyBox::take(boxed);
//!
//! // Let's make an objectively superior string, and put it into the original
//! // box.
//! let superior = "Objectively superior string!".to_string();
//!
//! // Now we have our superior string in the box!
//! let boxed = empty.put(superior); 
//!
//! assert_eq!("Hello!", string);
//! assert_eq!("Objectively superior string!", &*boxed);
//! ```
//!
//! Creating an `EmptyBox` from a `Box` and then putting a `T` back into the
//! `EmptyBox` will avoid allocating a new `Box`, instead reusing whatever old
//! `Box` the `T` was `EmptyBox::take`n from.

use std::mem;
use std::ptr;


/// An "emptied" `Box`. Constructed via `EmptyBox::take()`, an `EmptyBox<T>` is
/// a `Box` from which the contents have been moved. This allows for reuse of the
/// `Box` via `EmptyBox::put()`, which moves the contents back in, turning the
/// `EmptyBox` back into a `Box<T>`.
pub struct EmptyBox<T> {
    ptr: *mut T,
}


impl<T> Drop for EmptyBox<T> {
    fn drop(&mut self) {
        let boxed = unsafe { Box::from_raw(self.ptr) };
        let inner = *boxed;
        mem::forget(inner);
    }
}


impl<T> EmptyBox<T> {
    /// Move the value out of the `Box`, creating a `T` and an `EmptyBox` which
    /// preserves the original `Box`'s allocation.
    pub fn take(bx: Box<T>) -> (T, EmptyBox<T>) {
        let ptr = Box::into_raw(bx);
        let t = unsafe { ptr::read(ptr) };
        (t, EmptyBox { ptr })
    }


    /// Restore a value to an `EmptyBox`, creating a new `Box` and reusing the
    /// allocation of whatever `Box` was destroyed to create the `EmptyBox`.
    pub fn put(self, t: T) -> Box<T> {
        let EmptyBox { ptr } = self;

        // `Drop` will be run otherwise - this is because even though we've
        // clearly moved out of `self`, it persists since the only field we
        // matched from it is `Copy`.
        mem::forget(self); 

        unsafe {
            ptr::write(ptr, t);
            Box::from_raw(ptr)
        }
    }
}


#[cfg(test)]
mod test {
    use std::cell::Cell;

    use super::*;


    #[derive(Clone)]
    pub struct DropCounter<'a>(&'a Cell<usize>);

    impl<'a> Drop for DropCounter<'a> {
        fn drop(&mut self) {
            let prev = self.0.get();
            self.0.set(prev + 1);
        }
    }


    #[test]
    fn drop_counter() {
        let counter = Cell::new(0);

        mem::drop(DropCounter(&counter));
        mem::drop(DropCounter(&counter));

        let dc = DropCounter(&counter);

        assert_eq!(counter.get(), 2);

        mem::drop(dc);

        assert_eq!(counter.get(), 3);
    }


    #[test]
    fn no_drop() {
        let counter = Cell::new(0);

        let dc = {
            let boxed = Box::new(DropCounter(&counter));
            EmptyBox::take(boxed).0
        };

        assert_eq!(counter.get(), 0);

        mem::drop(dc);
    }


    #[test]
    fn two_drop() {
        let counter = Cell::new(0);

        let boxed = Box::new(DropCounter(&counter));
        let (dc, empty) = EmptyBox::take(boxed);

        mem::drop(dc);

        mem::drop(empty.put(DropCounter(&counter)));

        assert_eq!(counter.get(), 2);
    }
}