diff --git a/src/lib.rs b/src/lib.rs index 61d31981..df16f441 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,6 +175,8 @@ pub use webcore::instance_of::InstanceOf; pub use webcore::reference_type::ReferenceType; pub use webcore::serialization::JsSerialize; +pub use webcore::discard::DiscardOnDrop; + #[cfg(feature = "experimental_features_which_may_break_on_minor_version_bumps")] pub use webcore::promise::{Promise, DoneHandle}; diff --git a/src/webcore/discard.rs b/src/webcore/discard.rs new file mode 100644 index 00000000..4c2e9f3c --- /dev/null +++ b/src/webcore/discard.rs @@ -0,0 +1,160 @@ +use discard; +use discard::Discard; +use std::ops::{Deref, DerefMut}; + + +/// If you have a value which implements [`Discard`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html), you can use +/// [`DiscardOnDrop::new(value)`](struct.DiscardOnDrop.html#method.new) which will wrap the value. +/// When the wrapper is dropped it will automatically call [`value.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard). +/// +/// You can use the [`leak`](#method.leak) method to unwrap it (which returns `value`). This causes +/// it to no longer call [`discard`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard) when it is dropped, which +/// means it will usually leak memory unless you manually call [`discard`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard). +#[must_use = " + + The DiscardOnDrop is unused, which causes it to be immediately discarded. + You probably don't want that to happen. + + How to fix this: + + * Store the DiscardOnDrop in a variable or data structure. + + * Or use the leak() method which will cause it to not be + discarded (this will usually leak memory!) + + See the documentation for more details. +"] +#[derive(Debug)] +pub struct DiscardOnDrop< A: Discard >( discard::DiscardOnDrop< A > ); + +impl< A: Discard > DiscardOnDrop< A > { + /// Creates a new `DiscardOnDrop`. + /// + /// When the `DiscardOnDrop` is dropped it will automatically call [`discarder.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard). + #[inline] + pub fn new( discarder: A ) -> Self { + DiscardOnDrop( discard::DiscardOnDrop::new( discarder ) ) + } + + /// Returns the wrapped `discarder`. + /// + /// It will no longer automatically call [`discarder.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard), so this will usually leak + /// memory unless you manually call [`discarder.discard()`](https://docs.rs/discard/%5E1.0.3/discard/trait.Discard.html#tymethod.discard). + #[inline] + pub fn leak( self ) -> A { + discard::DiscardOnDrop::leak( self.0 ) + } +} + +impl< A: Discard > Deref for DiscardOnDrop< A > { + type Target = A; + + #[inline] + fn deref( &self ) -> &Self::Target { + &*self.0 + } +} + +impl< A: Discard > DerefMut for DiscardOnDrop< A > { + #[inline] + fn deref_mut( &mut self ) -> &mut Self::Target { + &mut *self.0 + } +} + + +#[cfg(test)] +mod tests { + use discard::Discard; + use super::DiscardOnDrop; + use std::rc::Rc; + use std::cell::Cell; + + struct Foo( Rc< Cell< bool > > ); + + impl Foo { + fn new() -> Self { + Foo( Rc::new( Cell::new( false ) ) ) + } + + fn dropped( &self ) -> Rc< Cell< bool > > { + self.0.clone() + } + + fn as_mut( &mut self ) -> &mut Self { + self + } + } + + impl Discard for Foo { + fn discard( self ) { + self.0.set( true ); + } + } + + + #[test] + fn unused() { + Foo::new(); + } + + #[test] + fn unused_discard_on_drop() { + DiscardOnDrop::new( Foo::new() ); + } + + #[test] + fn discard() { + let foo = Foo::new(); + + let dropped = foo.dropped(); + + assert_eq!( dropped.get(), false ); + foo.discard(); + assert_eq!( dropped.get(), true ); + } + + #[test] + fn no_discard() { + let foo = Foo::new(); + + let dropped = foo.dropped(); + + assert_eq!( dropped.get(), false ); + drop( foo ); + assert_eq!( dropped.get(), false ); + } + + #[test] + fn discard_on_drop() { + let foo = DiscardOnDrop::new( Foo::new() ); + + let dropped = foo.dropped(); + + assert_eq!( dropped.get(), false ); + drop( foo ); + assert_eq!( dropped.get(), true ); + } + + #[test] + fn leak() { + let foo = DiscardOnDrop::new(Foo::new()); + + let dropped = foo.dropped(); + + assert_eq!( dropped.get(), false ); + drop( foo.leak() ); + assert_eq!( dropped.get(), false ); + } + + #[test] + fn deref_mut() { + let mut foo = DiscardOnDrop::new( Foo::new() ); + + let dropped = foo.as_mut().dropped(); + + assert_eq!( dropped.get(), false ); + drop( foo.leak() ); + assert_eq!( dropped.get(), false ); + } +} diff --git a/src/webcore/mod.rs b/src/webcore/mod.rs index 7088fb94..e81ae928 100644 --- a/src/webcore/mod.rs +++ b/src/webcore/mod.rs @@ -17,6 +17,7 @@ pub mod once; pub mod instance_of; pub mod reference_type; pub mod promise; +pub mod discard; #[cfg(feature = "futures")] pub mod promise_future; diff --git a/src/webcore/promise.rs b/src/webcore/promise.rs index 03a29b81..ce10b5b2 100644 --- a/src/webcore/promise.rs +++ b/src/webcore/promise.rs @@ -1,8 +1,9 @@ use std; +use discard::Discard; use webcore::once::Once; use webcore::value::{Value, Reference}; use webcore::try_from::{TryInto, TryFrom}; -use discard::{Discard, DiscardOnDrop}; +use webcore::discard::DiscardOnDrop; #[cfg(feature = "futures")] use webcore::serialization::JsSerialize; @@ -176,10 +177,9 @@ impl Promise { /// * Keep the [`DoneHandle`](struct.DoneHandle.html) alive until after the `callback` is called (by storing it in a /// variable or data structure). /// - /// * Use the [`DiscardOnDrop::leak`](https://docs.rs/discard/1.*/discard/struct.DiscardOnDrop.html#method.leak) function to leak the - /// [`DoneHandle`](struct.DoneHandle.html). If the `Promise` never succeeds or fails then this ***will*** leak the - /// memory of the callback, so only use [`DiscardOnDrop::leak`](https://docs.rs/discard/1.*/discard/struct.DiscardOnDrop.html#method.leak) - /// if you need to. + /// * Use the [`leak`](struct.DiscardOnDrop.html#method.leak) method to leak the [`DoneHandle`](struct.DoneHandle.html). + /// If the `Promise` never succeeds or fails then this ***will*** leak the memory of the callback, so only use + /// [`leak`](struct.DiscardOnDrop.html#method.leak) if you need to. /// /// # Examples /// @@ -197,14 +197,12 @@ impl Promise { /// Leak the [`DoneHandle`](struct.DoneHandle.html) and `callback`: /// /// ```rust - /// use discard::DiscardOnDrop; - /// - /// DiscardOnDrop::leak(promise.done(|result| { + /// promise.done(|result| { /// match result { /// Ok(success) => { ... }, /// Err(error) => { ... }, /// } - /// })); + /// }).leak(); /// ``` /// /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) diff --git a/src/webcore/promise_future.rs b/src/webcore/promise_future.rs index 4097c5c3..40fb8494 100644 --- a/src/webcore/promise_future.rs +++ b/src/webcore/promise_future.rs @@ -5,7 +5,7 @@ use webapi::error; use futures::{Future, Poll, Async}; use futures::unsync::oneshot::Receiver; use webcore::promise_executor::spawn; -use discard::DiscardOnDrop; +use webcore::discard::DiscardOnDrop; use super::promise::{Promise, DoneHandle};