Crate discard

Source
Expand description

There are situations where you need to intentionally leak some memory but not other memory. This crate can help!

But before I explain, you are probably wondering: why would I want to leak memory in the first place?

There are certain rare situations where leaking memory is either desirable or necessary.

As an example, let’s say I am using stdweb, which lets me use JavaScript APIs in Rust.

So I write some code like this:

node.add_event_listener(|event: ClickEvent| {
    // ...
});

Seems reasonable, right? But there’s a problem: the add_event_listener method returns an EventListenerHandle, and when the EventListenerHandle is dropped it will remove the event listener.

Because I’m not using the EventListenerHandle, it is immediately dropped, so it receives no events!

Okay, no problem, just use std::mem::forget:

// This will automatically remove the event listener when `handle` is dropped
let handle = node.add_event_listener(|event: ClickEvent| {
    // ...
});

// Now it will no longer remove the event listener
std::mem::forget(handle);

Now the event listener will remain alive forever, which is what I want.

But there’s two problems with this:

  1. I want it to keep the event listener alive forever, but I also want it to clean up any unused internal memory. Using std::mem::forget causes it to leak all of the memory, which is wasteful.

  2. There are situations where I want to leak the event listener, and then later unleak it. That’s not possible with std::mem::forget.

The solution to all of these problems is:

  1. The EventListenerHandle should not implement the Drop trait.

  2. The EventListenerHandle should implement the Discard trait instead.

  3. The add_event_listener method should return DiscardOnDrop<EventListenerHandle>.

Now let’s look at what is possible:

// This will automatically remove the event listener when `handle` is dropped
let handle = node.add_event_listener(|event: ClickEvent| {
    // ...
});

// Now it will no longer remove the event listener, this is similar to `std::mem::forget`
let leaked = DiscardOnDrop::leak(handle);

// Now it will remove the event listener, even though it was leaked
leaked.discard();

There’s two huge differences between DiscardOnDrop::leak and std::mem::forget:

  1. std::mem::forget leaks all of the memory, DiscardOnDrop::leak leaks the minimal amount of memory: unused memory is properly cleaned up.

  2. With std::mem::forget you cannot clean up a value after it has been leaked, but with DiscardOnDrop::leak you can manually discard the value even after it has been leaked.

Most of the time you don’t need to worry about any of this: DiscardOnDrop will automatically call discard when it is dropped, so in that situation Discard behaves the same as Drop.

So you can use normal Rust idioms, and everything works as you would expect. You only need to worry about Discard when you need to intentionally leak some memory.

Structs§

Traits§

  • This trait is very similar to Drop: it allows for cleaning up memory and resources when they are no longer needed.