drop_move/
lib.rs

1#![warn(missing_docs)]
2#![no_std]
3
4/*!
5
6This crate allows implementing [`Drop`] as "pass by move". Here is an example of how this can be
7used to call a [`FnOnce`] from [`drop`].
8
9```
10use drop_move::{drop_move_wrap, DropMove, DropHandle};
11
12drop_move_wrap! {
13    /// Runs a function when dropped.
14    #[derive(Clone)]
15    pub struct DropGuard<F: FnOnce()>(DropGuardInner {
16        func: F,
17    });
18}
19
20impl<F: FnOnce()> DropMove for DropGuardInner<F> {
21    fn drop_move(self_: DropHandle<Self>) {
22        (DropHandle::into_inner(self_).func)()
23    }
24}
25
26impl<F: FnOnce()> DropGuard<F> {
27    pub fn new(f: F) -> Self {
28        DropGuardInner { func: f }.into()
29    }
30}
31
32let mut x: u32 = 0;
33{
34    let y = Box::new(&mut x); // Box is not Copy, so the closure will only be FnOnce.
35    let guard = DropGuard::new(move || **y += 1);
36}
37
38assert_eq!(x, 1);
39```
40
41By implementing the [`DropMove`] trait, we were able to have `func` run when the `DropGuard` goes
42out of scope. The usual [`Drop`] trait only allows `drop(&mut self)`, which does not allow moving
43the members of the `DropGuard`, as is required to call a [`FnOnce`]. The reason they do not allow
44`drop(self)` is that it would be too easy to accidentally end up dropping `self`, leading to an
45infinite loop. According to Rust's usual semantics `self` would be dropped at the end of the scope,
46and even if a special case were added for `drop(self)` it could still easily happen in a function
47called by `drop`.
48
49These problems are mostly avoided by wrapping `self` in a [`DropHandle`], which will only drop each
50member of the structure when it goes out of scope, rather than calling `drop` recursively.
51Semantically, [`drop_move`](DropMove::drop_move) can be thought of as destructuring `DropGuard`.
52Each unmoved member will be dropped when it goes out of scope. These members can be accessed by
53value through [`into_inner`](DropHandle::into_inner). The original `DropGuard` can be obtained from
54[`into_outer`](DropHandle::into_outer), but you must be careful to avoid infinite recursion when
55using this.
56
57Given this destructuring viewpoint, it should be no surprise that `drop_move` also supports
58destructuring, which is normally not allowed for types that implement [`Drop`]. Here, we can
59convert the `DropGuard` back into the function it contains.
60
61```
62# use drop_move::{drop_move_wrap, DropMove, DropHandle};
63#
64# drop_move_wrap! {
65#     #[derive(Clone)]
66#     pub struct DropGuard<F: FnOnce()>(DropGuardInner {
67#         func: F,
68#     });
69# }
70#
71# impl<F: FnOnce()> DropMove for DropGuardInner<F> {
72#     fn drop_move(self_: DropHandle<Self>) {
73#         (DropHandle::into_inner(self_).func)()
74#     }
75# }
76impl<F: FnOnce()> DropGuard<F> {
77    /// Extract the function.
78    pub fn into_inner(self) -> F {
79        let inner: DropGuardInner<F> = self.into();
80        inner.func
81    }
82}
83```
84
85How this works is that [`drop_move_wrap!`] expands into two structure definitions.
86
87```ignore
88# use drop_move::DropMoveWrapper;
89/// Runs a function when dropped.
90#[derive(Clone)]
91pub struct DropGuard<F: FnOnce()>(DropMoveWrapper<DropGuardInner<F>>);
92
93/// Runs a function when dropped.
94#[derive(Clone)]
95struct DropGuardInner<F: FnOnce()> {
96    func: F,
97};
98```
99
100The outer structure `DropGuard` provides the public interface, while `DropGuardInner` contains the
101actual members of the `struct`. Neither will implement [`Drop`]. Instead, [`DropMoveWrapper`] will
102implement [`Drop`] based on the [`DropMove`] you provide. The structure members can be borrowed
103from a `DropGaurd` using `&self.0.func`, because [`DropMoveWrapper`] implements [`Deref`]. They can
104moved by converting the `DropGaurd` to a `DropGuardInner` with
105[`DropMoveWrapper::into_inner(self.0)`](DropMoveWrapper::into_inner) or
106[`self.into()`](Into::into).
107
108Notice that the doc comments and attributes have been duplicated for both structures. In fact, doc
109comments are treated as [attributes](https://stackoverflow.com/a/33999625/4071916) by the compiler.
110
111The macro also creates a few trait implementations.
112
113```
114#  use drop_move::{DropMove, DropMoveTypes, DropMoveWrapper, DropHandle};
115# #[derive(Clone)]
116# pub struct DropGuard<F: FnOnce()>(DropMoveWrapper<DropGuardInner<F>>);
117#
118# #[derive(Clone)]
119# struct DropGuardInner<F: FnOnce()> {
120#     func: F,
121# };
122#
123impl<F: FnOnce()> From<DropGuard<F>> for DropGuardInner<F> {
124    fn from(x: DropGuard<F>) -> Self {
125        DropMoveWrapper::into_inner(x.0)
126    }
127}
128
129impl<F: FnOnce()> From<DropGuardInner<F>> for DropGuard<F> {
130    fn from(x: DropGuardInner<F>) -> Self {
131        Self(DropMoveWrapper::new(x))
132    }
133}
134
135impl<F: FnOnce()> DropMoveTypes for DropGuardInner<F>
136{
137    type Outer = DropGuard<F>;
138}
139#
140# impl<F: FnOnce()> DropMove for DropGuardInner<F> {
141#     fn drop_move(self_: DropHandle<Self>) {
142#         (DropHandle::into_inner(self_).func)()
143#     }
144# }
145#
146# impl<F: FnOnce()> DropGuard<F> {
147#     pub fn new(f: F) -> Self {
148#         DropGuard(DropMoveWrapper::new(DropGuardInner { func: f }))
149#     }
150# }
151#
152# let mut x: u32 = 0;
153# {
154#     let y = Box::new(&mut x);
155#     // Box is not Copy, so the closure will only be FnOnce.
156#     let guard = DropGuard::new(move || **y += 1);
157# }
158#
159# assert_eq!(x, 1);
160```
161
162The implementation of [`DropMoveTypes`] lets [`DropMoveWrapper`] and [`DropHandle`] know the
163relationship between `DropGuard` and `DropGuardInner`. It is implemented on the inner structure
164because this will keep the implementation private in the common case that the inner structure is
165private but the outer is public. The [`From`] implementations are so that they know how to convert
166back and forth, and also function as convenience methods for creating and destructuring
167`DropGuard`s.
168
169You may be wondering why `drop_move` takes a [`DropHandle`] rather than just passing the inner
170structure `DropGuardInner`, which would behave correctly for destructuring and would drop the
171members individually. However, you wouldn't easily be able to call a `&self` or `&mut self`
172function, which would want an instance of `DropGuard` instead. It would require reconstructing the
173[`DropGuard`] again so that it can be borrowed, then carefully destructuring it after the call to
174avoid infinite `drop` recursion. [`DropHandle`] allows you to avoid this error prone construction
175as it implements [`Deref`] for the outer structure, so you can call its methods directly.
176
177See [`drop_move_wrap!`] for the macro's full supported syntax. See the source for [`DropGuard`] for
178the full example.
179*/
180
181use core::mem;
182use core::ops::Deref;
183use core::ops::DerefMut;
184use mem::ManuallyDrop;
185
186/// Tracks the relationship between an inner `struct` and outer `struct` generated by
187/// [`drop_move_wrap!`].
188///
189/// It is implemented for the inner structure, and `Self::Outer` is set to be the outer structure.
190/// This is separated from [`DropMove`] so that it can be implemented automatically by the macro.
191pub trait DropMoveTypes: Sized + Into<<Self as DropMoveTypes>::Outer>
192where
193    Self::Outer: Into<Self>,
194{
195    /// The corresponding outer structure.
196    type Outer;
197}
198
199/// A variant of [`Drop`] that allows moving out of the value being dropped.
200///
201/// This trait must be implemented by the inner structure generated by [`drop_move_wrap!`].
202pub trait DropMove: DropMoveTypes {
203    /// Drop the value, by move instead of by reference.
204    fn drop_move(self_: DropHandle<Self>) {
205        mem::drop(self_);
206    }
207}
208
209mod drop_handle;
210pub use drop_handle::*;
211
212mod drop_move_wrap;
213pub use drop_move_wrap::*;
214
215mod drop_guard;
216pub use drop_guard::*;
217
218#[cfg(test)]
219mod test {
220    use super::*;
221
222    extern crate std;
223    use core::cell::RefCell;
224    use core::marker::PhantomData;
225    use std::boxed::Box;
226    use std::rc::Rc;
227    use std::vec::Vec;
228
229    drop_move_wrap! {
230        pub struct ReturnToSender<T>(pub ReturnToSenderInner(T, Rc<RefCell<Vec<T>>>))
231        where
232            T: Clone;
233    }
234
235    impl<T: Clone> DropMove for ReturnToSenderInner<T> {
236        fn drop_move(self_: DropHandle<Self>) {
237            let inner = DropHandle::into_inner(self_);
238            inner.1.borrow_mut().push(inner.0);
239        }
240    }
241
242    #[test]
243    fn return_to_sender() {
244        let free_list = Rc::new(RefCell::new(Vec::new()));
245
246        let rts = ReturnToSender::from(ReturnToSenderInner(Box::new(234u32), free_list.clone()));
247        assert!(free_list.borrow().len() == 0);
248
249        mem::drop(rts);
250
251        assert!(free_list.borrow().len() == 1);
252        assert!(*free_list.borrow()[0] == 234);
253    }
254
255    drop_move_wrap! {
256        #[derive(Clone)]
257        {
258            #[derive(PartialEq)]
259        }
260        pub enum WrapStressTest<'a, T: Deref : 'a>(
261            #[derive(PartialOrd)]
262            #[allow(dead_code)]
263            pub(crate) WrapStressTest1 {
264                Foo(PhantomData<&'a ()>),
265                Bar(T),
266            }
267        )
268        where
269            T::Target: 'a;
270    }
271
272    impl<'a, T: Deref> DropMove for WrapStressTest1<'a, T>
273    where
274        T: 'a,
275        T::Target: 'a,
276    {
277    }
278
279    impl<'a, T: Deref> PartialEq for WrapStressTest1<'a, T>
280    where
281        T: 'a,
282        T::Target: 'a,
283    {
284        fn eq(&self, _other: &Self) -> bool {
285            false
286        }
287    }
288}