Expand description

Crate to check if a variable got correctly dropped. This crate is mostly useful in unit tests for code involving ManuallyDrop, MaybeUninit, unsafe memory management, custom containers, and more.

Concepts

The main struct of this crate is DropTracker. Once you initialize a tracker, you call DropTracker::track on it to get a DropItem. Each drop item is identified by a key; the key can be used at any time to check the state of the item and see if it’s alive or if it has been dropped.

Examples

This is how you would test that a container like Vec drops all its items when the container is dropped:

use drop_tracker::DropTracker;

let mut tracker = DropTracker::new();

// Create a new vector and add a bunch of elements to it. The elements in this case are
// identified by integer key (1, 2, 3), but any hashable type would work.
//
// Labels are only used to identify the elements within the tracker, and are not passed
// around. In this example, the integers 1, 2 and 3 are not placed into the vector, but are
// kept into the DropTracker.
let v = vec![tracker.track(1),
             tracker.track(2),
             tracker.track(3)];

// Assert that all elements in the vector are alive
tracker.all_alive(1..=3)
       .expect("expected all elements to be alive");

// Once the vector is dropped, all items should be dropped with it
drop(v);
tracker.all_dropped(1..=3)
       .expect("expected all elements to be dropped");

This is how you would test a struct that involves MaybeUninit:

ⓘ
use std::mem::MaybeUninit;

struct MyOption<T> {
    set: bool,
    data: MaybeUninit<T>,
}

impl<T> MyOption<T> {
    fn none() -> Self {
        Self { set: false, data: MaybeUninit::uninit() }
    }

    fn some(x: T) -> Self {
        Self { set: true, data: MaybeUninit::new(x) }
    }
}

// BUG: MyOption<T> does not implement Drop!
// BUG: The instance inside `data` may be initialized but not be properly destructed!

// BUG: The following code will silently leak memory:
let opt = MyOption::some(String::from("hello"));
drop(opt); // the String does not get deallocated

// DropTracker is able to catch this sort of bugs:
use drop_tracker::DropTracker;

let mut tracker = DropTracker::new();
let opt = MyOption::some(tracker.track("item"));

tracker.state(&"item")
       .alive()
       .expect("item is expected to be alive"); // works

drop(opt);

tracker.state(&"item")
       .dropped()
       .expect("item is expected to be dropped"); // panics, meaning that the bug was detected

Double drop

DropItem will panic if it gets dropped twice or more, as this is generally a bug and may cause undefined behavior. This feature can be used to identify bugs with code using ManuallyDrop, MaybeUninit or std::ptr::drop_in_place, like in the following example:

ⓘ
use std::ptr;
use drop_tracker::DropTracker;

let mut tracker = DropTracker::new();
let mut item = tracker.track("something");

unsafe { ptr::drop_in_place(&mut item); } // ok
unsafe { ptr::drop_in_place(&mut item); } // panic!

Structs

Error signaling that an item was expected to have been dropped, but it’s alive.

Error returned when trying to place multiple items with the same key inside the same DropTracker.

An item that will notify the parent DropTracker once it gets dropped.

Creates DropItems and tracks their state.

Error signaling that an item was expected to be alive, but it was dropped.

Error returned when failing to assert that a set of items is all alive.

Error returned when failing to assert that a set of items is all dropped.

Error returned when failing to query the status of an item with a key that is not known to DropTracker.

Enums

A type that represents the state of a DropItem: either alive or dropped.