[go: up one dir, main page]

discard/
lib.rs

1//! There are situations where you need to intentionally leak *some*
2//! memory but not *other* memory. This crate can help!
3//!
4//! But before I explain, you are probably wondering: why would I
5//! want to leak memory in the first place?
6//!
7//! There are certain rare situations where leaking memory is either
8//! desirable or *necessary*.
9//!
10//! As an example, let's say I am using [stdweb](https://crates.io/crates/stdweb),
11//! which lets me use JavaScript APIs in Rust.
12//!
13//! So I write some code like this:
14//!
15//! ```rust,ignore
16//! node.add_event_listener(|event: ClickEvent| {
17//!     // ...
18//! });
19//! ```
20//!
21//! Seems reasonable, right? But there's a problem: the `add_event_listener` method
22//! returns an `EventListenerHandle`, and when the `EventListenerHandle` is dropped it
23//! will remove the event listener.
24//!
25//! Because I'm not using the `EventListenerHandle`, it is immediately dropped, so it
26//! receives no events!
27//!
28//! Okay, no problem, just use [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html):
29//!
30//! ```rust,ignore
31//! // This will automatically remove the event listener when `handle` is dropped
32//! let handle = node.add_event_listener(|event: ClickEvent| {
33//!     // ...
34//! });
35//!
36//! // Now it will no longer remove the event listener
37//! std::mem::forget(handle);
38//! ```
39//!
40//! Now the event listener will remain alive forever, which is what I want.
41//!
42//! But there's two problems with this:
43//!
44//! 1) I want it to keep the event listener alive forever, but I *also* want it to clean up any
45//!    unused internal memory. Using [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html)
46//!    causes it to leak ***all*** of the memory, which is wasteful.
47//!
48//! 2) There are situations where I want to leak the event listener, and then later *unleak* it.
49//!    That's not possible with [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html).
50//!
51//! The solution to all of these problems is:
52//!
53//! 1. The `EventListenerHandle` should ***not*** implement the [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) trait.
54//!
55//! 2. The `EventListenerHandle` should implement the [`Discard`](trait.Discard.html) trait instead.
56//!
57//! 3. The `add_event_listener` method should return `DiscardOnDrop<EventListenerHandle>`.
58//!
59//! Now let's look at what is possible:
60//!
61//! ```rust,ignore
62//! // This will automatically remove the event listener when `handle` is dropped
63//! let handle = node.add_event_listener(|event: ClickEvent| {
64//!     // ...
65//! });
66//!
67//! // Now it will no longer remove the event listener, this is similar to `std::mem::forget`
68//! let leaked = DiscardOnDrop::leak(handle);
69//!
70//! // Now it will remove the event listener, even though it was leaked
71//! leaked.discard();
72//! ```
73//!
74//! There's two huge differences between [`DiscardOnDrop::leak`](struct.DiscardOnDrop.html#method.leak)
75//! and [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html):
76//!
77//! 1) [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html) leaks ***all*** of the memory,
78//!    [`DiscardOnDrop::leak`](struct.DiscardOnDrop.html#method.leak) leaks the *minimal*
79//!    amount of memory: unused memory is properly cleaned up.
80//!
81//! 2) With [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html) you cannot
82//!    clean up a value after it has been leaked, but with
83//!    [`DiscardOnDrop::leak`](struct.DiscardOnDrop.html#method.leak) you can manually discard
84//!    the value even after it has been leaked.
85//!
86//! Most of the time you don't need to worry about any of this:
87//! [`DiscardOnDrop`](struct.DiscardOnDrop.html) will automatically call
88//! [`discard`](trait.Discard.html#tymethod.discard) when it is dropped, so in that situation
89//! [`Discard`](trait.Discard.html) behaves the same as [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html).
90//!
91//! So you can use normal Rust idioms, and everything works as you would expect. You only need to
92//! worry about [`Discard`](trait.Discard.html) when you need to intentionally leak some memory.
93
94#![deny(
95    missing_docs,
96    missing_debug_implementations
97)]
98
99use std::ops::{Deref, DerefMut};
100use std::mem::ManuallyDrop;
101
102/// This trait is very similar to [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html):
103/// it allows for cleaning up memory and resources when they are no longer needed.
104///
105/// However, unlike [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) you need to
106/// manually call the [`discard`](#tymethod.discard) method.
107///
108/// It is extremely common to use [`DiscardOnDrop`](struct.DiscardOnDrop.html), which will cause it
109/// to automatically call the [`discard`](#tymethod.discard) method when it is dropped. In that situation
110/// [`Discard`](trait.Discard.html) behaves exactly the same as [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html).
111///
112/// You can use [`DiscardOnDrop::leak`](struct.DiscardOnDrop.html#method.leak) to intentionally leak the value
113/// (which causes [`discard`](#tymethod.discard) to not be called), and then later you can manually call
114/// [`discard`](#tymethod.discard) to clean up the resources, even after the resources have been leaked.
115///
116/// See the [module documentation](index.html) for more details.
117pub trait Discard {
118    /// This consumes the value and cleans up any memory / resources / etc. that the value was
119    /// using.
120    ///
121    /// See the [module documentation](index.html) for more details.
122    fn discard(self);
123}
124
125
126/// If you have a value which implements [`Discard`](trait.Discard.html), you can use
127/// [`DiscardOnDrop::new(value)`](struct.DiscardOnDrop.html#method.new) which will wrap the value.
128/// When the wrapper is dropped it will automatically call [`value.discard()`](trait.Discard.html#tymethod.discard).
129///
130/// You can use the [`DiscardOnDrop::leak`](#method.leak) function to unwrap it (which returns `value`). This causes
131/// it to no longer call [`discard`](trait.Discard.html#tymethod.discard) when it is dropped, which
132/// means it will leak memory unless you manually call [`discard`](trait.Discard.html#tymethod.discard).
133///
134/// See the [module documentation](index.html) for more details.
135#[must_use = "
136The DiscardOnDrop is unused, which causes it to be immediately discarded.
137You probably don't want that to happen.
138
139How to fix this:
140
141  * Store the DiscardOnDrop in a variable or data structure.
142
143  * Or use the DiscardOnDrop::leak function which will cause it to not be
144    discarded (this *will* leak memory!).
145
146See the DiscardOnDrop documentation for more details."]
147#[derive(Debug)]
148pub struct DiscardOnDrop<A: Discard>(ManuallyDrop<A>);
149
150impl<A: Discard> DiscardOnDrop<A> {
151    /// Creates a new `DiscardOnDrop`.
152    ///
153    /// When the `DiscardOnDrop` is dropped it will automatically call [`discarder.discard()`](trait.Discard.html#tymethod.discard).
154    ///
155    /// See the [module documentation](index.html) for more details.
156    #[inline]
157    pub fn new(discarder: A) -> Self {
158        DiscardOnDrop(ManuallyDrop::new(discarder))
159    }
160
161    /// Returns the wrapped `discarder`.
162    ///
163    /// It will no longer automatically call [`discarder.discard()`](trait.Discard.html#tymethod.discard), so this ***will*** leak memory
164    /// unless you manually call [`discarder.discard()`](trait.Discard.html#tymethod.discard).
165    ///
166    /// See the [module documentation](index.html) for more details.
167    ///
168    /// This is implemented as a function (*not* a method) so that way it doesn't interfere with any of the
169    /// methods on `discarder`.
170    #[inline]
171    pub fn leak(this: Self) -> A {
172        // We want to move the `A` out of `this`, but that's not allowed because `this` implements `Drop`
173        // (and we must also avoid calling `drop()` on `this` or else `A` would get dropped twice).
174        //
175        // We can do that move by using the unsafe function std::ptr::read(),
176        // and then use `mem::forget()` on `this` so it never gets dropped. The `A` will get dropped by the caller.
177        //
178        // TODO verify that this is completely safe
179        unsafe {
180            let value: A = ::std::ptr::read(this.0.deref());
181            ::std::mem::forget(this);
182            value
183        }
184    }
185}
186
187impl<A: Discard> Drop for DiscardOnDrop<A> {
188    #[inline]
189    fn drop(&mut self) {
190        // This only gets called if there is still a valid `A` inside the `ManuallyDrop`,
191        // since in `leak()` we prevent `drop()` from being called.
192        //
193        // Similar to `leak()`, we want to move `A` out of `self` but again we can't,
194        // this time because we only have a mutable reference, not a value.
195        //
196        // The solution is the same though, use `std::ptr::read()` to do the move,
197        // the `A` will get dropped by `.discard()` and since we wrapped it in `ManuallyDrop`,
198        // it won't be dropped again at the end of this function.
199        //
200        // TODO verify that this is completely safe
201        unsafe {
202            let value: A = ::std::ptr::read(self.0.deref());
203            value.discard();
204        }
205    }
206}
207
208impl<A: Discard> Deref for DiscardOnDrop<A> {
209    type Target = A;
210
211    #[inline]
212    fn deref(&self) -> &Self::Target {
213        self.0.deref()
214    }
215}
216
217impl<A: Discard> DerefMut for DiscardOnDrop<A> {
218    #[inline]
219    fn deref_mut(&mut self) -> &mut Self::Target {
220        self.0.deref_mut()
221    }
222}
223
224
225#[cfg(test)]
226mod tests {
227    use super::{Discard, DiscardOnDrop};
228    use std::rc::Rc;
229    use std::cell::Cell;
230
231    struct Foo(Rc<Cell<bool>>);
232
233    impl Foo {
234        fn new() -> Self {
235            Foo(Rc::new(Cell::new(false)))
236        }
237
238        fn dropped(&self) -> Rc<Cell<bool>> {
239            self.0.clone()
240        }
241
242        fn as_mut(&mut self) -> &mut Self {
243            self
244        }
245    }
246
247    impl Discard for Foo {
248        fn discard(self) {
249            self.0.set(true);
250        }
251    }
252
253
254    #[test]
255    fn unused() {
256        Foo::new();
257    }
258
259    #[test]
260    fn unused_discard_on_drop() {
261        DiscardOnDrop::new(Foo::new());
262        DiscardOnDrop::new(Foo::new());
263    }
264
265    #[test]
266    fn discard() {
267        let foo = Foo::new();
268
269        let dropped = foo.dropped();
270
271        assert_eq!(dropped.get(), false);
272        foo.discard();
273        assert_eq!(dropped.get(), true);
274    }
275
276    #[test]
277    fn no_discard() {
278        let foo = Foo::new();
279
280        let dropped = foo.dropped();
281
282        assert_eq!(dropped.get(), false);
283        drop(foo);
284        assert_eq!(dropped.get(), false);
285    }
286
287    #[test]
288    fn discard_on_drop() {
289        let foo = DiscardOnDrop::new(Foo::new());
290
291        let dropped = foo.dropped();
292
293        assert_eq!(dropped.get(), false);
294        drop(foo);
295        assert_eq!(dropped.get(), true);
296    }
297
298    #[test]
299    fn leak() {
300        let foo = DiscardOnDrop::new(Foo::new());
301
302        let dropped = foo.dropped();
303
304        assert_eq!(dropped.get(), false);
305        drop(DiscardOnDrop::leak(foo));
306        assert_eq!(dropped.get(), false);
307    }
308
309    #[test]
310    fn deref_mut() {
311        let mut foo = DiscardOnDrop::new(Foo::new());
312
313        let dropped = foo.as_mut().dropped();
314
315        assert_eq!(dropped.get(), false);
316        drop(DiscardOnDrop::leak(foo));
317        assert_eq!(dropped.get(), false);
318    }
319}