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
//! Safe helper for types that should implement both `Drop` and `.into_inner()` method. //! //! There's a repeating pattern where people want to have a type that does something on drop, //! but also want to be able to avoid dropping and destructure the type using some method that //! takes `self`. //! //! Furhter, sometimes people want to destructure the type in `drop` implementation, which isn't //! safe due to it being passed by mutable refernece. //! //! Hand-rolling `unsafe` code was neccessary until this crate existed. This crate takes the //! responsibility for ensuring that the drop impl is sound. More eyes, less bugs. The performace //! overhead of this crate is zero. //! //! This crate is `no_std`. //! //! # Example //! //! Let's say you want to have a special type that prints a string on drop, but with ability to //! take out the string without printing. This is how you would approach it using this crate: //! //! ``` //! // Not strictly neccessary, but helps encapsulate the inner representation. //! mod inner { //! // In fact, you could even avoid this newtype! //! // But in case you have something more complicated, you might need a newtype. //! pub(super) struct PrintOnDrop(pub(super) String); //! //! // You need this helper type to implement Drop. //! pub(super) enum PrintOnDropImpl {} //! //! // Drop is implemented a bit differently //! impl into_inner_drop::DetachedDrop for PrintOnDropImpl { //! // This type will be passed to your drop function by value (move). //! type Implementor = PrintOnDrop; //! //! // The drop implementation. The main difference is passing inner representation by-value. //! fn drop(value: Self::Implementor) { //! // You can destructucutre your type here if you want! //! // E.g. let string = value.0; //! println!("Dropping: {}", value.0); //! } //! } //! } //! //! use into_inner_drop::IntoInnerHelper; //! //! // Public representation //! //! /// A String that is printed when dropped. //! pub struct PrintOnDrop(IntoInnerHelper<inner::PrintOnDrop, inner::PrintOnDropImpl>); //! //! impl PrintOnDrop { //! /// Crates a string that is printed on drop. //! fn new(string: String) -> Self { //! PrintOnDrop(IntoInnerHelper::new(inner::PrintOnDrop(string))) //! } //! //! /// Takes out the string, preventing printing on drop. //! fn into_string(self) -> String { //! self.0.into_inner().0 //! } //! } //! //! fn main() { //! let print_on_drop = PrintOnDrop::new("Hello world!".to_owned()); //! let dont_print_on_drop = PrintOnDrop::new("Hello Rustceans!".to_owned()); //! //! let string = dont_print_on_drop.into_string(); //! println!("NOT on drop: {}", string); //! } //! //! ``` //! //! As you can see, the code has some boilerplate, but no `unsafe`. I'm already trying to come up //! with a macro to make it much easier. See the appropriate issue on GitHub to participate. #![no_std] use core::mem::ManuallyDrop; /// A replacement trait for providing Drop implementation. /// /// Since `self` is not used, it's recommended to create an empty enum and implement this trait for /// it. pub trait DetachedDrop { /// The inner type you want to implement Drop for. type Implementor; /// The drop implementation called by `IntoInnerHelper<Self::Implementor, Self>`. /// /// This function will only be called if `into_inner` was NOT called. fn drop(value: Self::Implementor); } /// The helper which allows you to implement `Drop` for your type while still allowing to take it /// apart by moving out. pub struct IntoInnerHelper<T, D> where D: DetachedDrop<Implementor=T> { inner: ManuallyDrop<T>, _phantom: core::marker::PhantomData<D>, } impl<T, D> IntoInnerHelper<T, D> where D: DetachedDrop<Implementor=T> { /// Creates the helper. pub fn new(inner: T) -> Self { IntoInnerHelper { inner: ManuallyDrop::new(inner), _phantom: Default::default(), } } /// Accesses the inner value. pub fn inner(&self) -> &T { &*self.inner } /// Accesses the inner value mutably. pub fn inner_mut(&mut self) -> &mut T { &mut *self.inner } /// Moves out the inner value. pub fn into_inner(self) -> T { unsafe { let inner = core::ptr::read(&*self.inner); core::mem::forget(self); inner } } } impl<T, D> Drop for IntoInnerHelper<T, D> where D: DetachedDrop<Implementor=T> { fn drop(&mut self) { unsafe { D::drop(core::ptr::read(&*self.inner)); } } } #[cfg(test)] mod tests { #[test] fn drop_once() { use super::{IntoInnerHelper, DetachedDrop}; enum Dummy {} impl DetachedDrop for Dummy { type Implementor = dropcheck::DropToken; fn drop(_: Self::Implementor) {} } let check = dropcheck::DropCheck::new(); let (drop_token, drop_state) = check.pair(); let helper = <IntoInnerHelper<_, Dummy>>::new(drop_token); assert!(drop_state.is_not_dropped()); core::mem::drop(helper); assert!(drop_state.is_dropped()); } #[test] fn into_inner() { use super::{IntoInnerHelper, DetachedDrop}; enum Dummy {} impl DetachedDrop for Dummy { type Implementor = dropcheck::DropToken; fn drop(_: Self::Implementor) {} } let check = dropcheck::DropCheck::new(); let (drop_token, drop_state) = check.pair(); let helper = <IntoInnerHelper<_, Dummy>>::new(drop_token); assert!(drop_state.is_not_dropped()); let inner = helper.into_inner(); assert!(drop_state.is_not_dropped()); core::mem::drop(inner); assert!(drop_state.is_dropped()); } }