Skip to content

Commit

Permalink
Adding in custom DiscardOnDrop struct
Browse files Browse the repository at this point in the history
  • Loading branch information
Pauan committed Mar 22, 2018
1 parent e645988 commit 84a95e8
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 10 deletions.
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
160 changes: 160 additions & 0 deletions src/webcore/discard.rs
Original file line number Diff line number Diff line change
@@ -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 );
}
}
1 change: 1 addition & 0 deletions src/webcore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 7 additions & 9 deletions src/webcore/promise.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
///
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/webcore/promise_future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};


Expand Down

0 comments on commit 84a95e8

Please sign in to comment.