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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//!
//! The **nodrop** crate has the following cargo feature flags:
//!
//! - `std`
//!   - Optional, enabled by default
//!   - Use libstd
//! - `use_needs_drop`
//!   - Optional
//!   - Requires Rust 1.21
//!   - Use `needs_drop` to skip overwriting if not necessary
//! - `use_union`
//!   - Optional
//!   - Requires nightly channel
//!   - Using untagged union, finally we have an implementation of `NoDrop` without hacks,
//!     for example the fact that `NoDrop<T>` never has a destructor anymore.
//!

#![cfg_attr(not(any(test, feature="std")), no_std)]
#[cfg(not(any(test, feature="std")))]
extern crate core as std;

#[cfg(feature = "use_union")]
extern crate nodrop_union as imp;

pub use imp::NoDrop;


#[cfg(not(feature = "use_union"))]
mod imp {
    use std::ptr;
    use std::mem;
    use std::ops::{Deref, DerefMut};

    /// repr(u8) - Make sure the non-nullable pointer optimization does not occur!
    #[repr(u8)]
    enum Flag<T> {
        Alive(T),
        // Dummy u8 field below, again to beat the enum layout opt
        Dropped(u8),
    }


    /// A type holding **T** that will not call its destructor on drop
    pub struct NoDrop<T>(Flag<T>);

    impl<T> NoDrop<T> {
        /// Create a new **NoDrop**.
        #[inline]
        pub fn new(value: T) -> NoDrop<T> {
            NoDrop(Flag::Alive(value))
        }

        /// Extract the inner value.
        ///
        /// Once extracted, the value can of course drop again.
        #[inline]
        pub fn into_inner(mut self) -> T {
            let inner = unsafe {
                ptr::read(&mut *self)
            };
            // skip Drop, so we don't even have to overwrite
            mem::forget(self);
            inner
        }
    }

    #[cfg(not(feature = "use_needs_drop"))]
    #[inline]
    fn needs_drop<T>() -> bool {
        true
    }

    #[cfg(feature = "use_needs_drop")]
    #[inline]
    fn needs_drop<T>() -> bool {
        unsafe {
            ::std::mem::needs_drop::<T>()
        }
    }

    impl<T> Drop for NoDrop<T> {
        fn drop(&mut self) {
            if needs_drop::<T>() {
                // inhibit drop
                unsafe {
                    ptr::write(&mut self.0, Flag::Dropped(0));
                }
            }
        }
    }

    impl<T> Deref for NoDrop<T> {
        type Target = T;

        // Use type invariant, always Flag::Alive.
        #[inline]
        fn deref(&self) -> &T {
            match self.0 {
                Flag::Alive(ref inner) => inner,
                _ => unsafe { debug_assert_unreachable() }
            }
        }
    }

    impl<T> DerefMut for NoDrop<T> {
        // Use type invariant, always Flag::Alive.
        #[inline]
        fn deref_mut(&mut self) -> &mut T {
            match self.0 {
                Flag::Alive(ref mut inner) => inner,
                _ => unsafe { debug_assert_unreachable() }
            }
        }
    }

    #[cfg(test)]
    #[test]
    fn test_no_nonnullable_opt() {
        // Make sure `Flag` does not apply the non-nullable pointer optimization
        // as Option would do.
        assert!(mem::size_of::<Flag<&i32>>() > mem::size_of::<&i32>());
        assert!(mem::size_of::<Flag<Vec<i32>>>() > mem::size_of::<Vec<i32>>());
    }

    // copying this code saves us microcrate deps
    #[inline]
    unsafe fn debug_assert_unreachable() -> ! {
        debug_assert!(false, "Reached unreachable section: this is a bug!");
        enum Void { }
        match *(1 as *const Void) { }
    }
}

#[cfg(test)]
mod tests {
    use super::NoDrop;

    #[test]
    fn test_drop() {
        use std::cell::Cell;

        let flag = &Cell::new(0);

        struct Bump<'a>(&'a Cell<i32>);

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

        {
            let _ = NoDrop::new([Bump(flag), Bump(flag)]);
        }
        assert_eq!(flag.get(), 0);

        // test something with the nullable pointer optimization
        flag.set(0);

        {
            let mut array = NoDrop::new(Vec::new());
            array.push(vec![Bump(flag)]);
            array.push(vec![Bump(flag), Bump(flag)]);
            array.push(vec![]);
            array.push(vec![Bump(flag)]);
            drop(array.pop());
            assert_eq!(flag.get(), 1);
            drop(array.pop());
            assert_eq!(flag.get(), 1);
            drop(array.pop());
            assert_eq!(flag.get(), 3);
        }

        // last one didn't drop.
        assert_eq!(flag.get(), 3);

        flag.set(0);
        {
            let array = NoDrop::new(Bump(flag));
            array.into_inner();
            assert_eq!(flag.get(), 1);
        }
        assert_eq!(flag.get(), 1);
    }
}