nodrop/lib.rs
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 187 188 189
//! A wrapper type to inhibit drop (destructor).
//!
//! ***Deprecated: Use ManuallyDrop or MaybeUninit instead!***
//!
//! 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);
}
}