drop_tracker/lib.rs
1//! Crate to check if a variable got correctly [dropped]. This crate is mostly useful in unit
2//! tests for code involving [`ManuallyDrop`], [`MaybeUninit`], unsafe memory management,
3//! custom containers, and more.
4//!
5//! [dropped]: https://doc.rust-lang.org/reference/destructors.html
6//! [`ManuallyDrop`]: std::mem::ManuallyDrop
7//! [`MaybeUninit`]: std::mem::MaybeUninit
8//!
9//! # Concepts
10//!
11//! The main struct of this crate is [`DropTracker`]. Once you initialize a tracker, you call
12//! [`DropTracker::track`] on it to get a [`DropItem`]. Each drop item is identified by a key;
13//! the key can be used at any time to check the state of the item and see if it's alive or if
14//! it has been dropped.
15//!
16//! # Examples
17//!
18//! This is how you would test that a container like [`Vec`] drops all its items when the container
19//! is dropped:
20//!
21//! ```
22//! use drop_tracker::DropTracker;
23//!
24//! let mut tracker = DropTracker::new();
25//!
26//! // Create a new vector and add a bunch of elements to it. The elements in this case are
27//! // identified by integer keys (1, 2, 3), but any hashable type would work.
28//! let v = vec![tracker.track(1),
29//! tracker.track(2),
30//! tracker.track(3)];
31//!
32//! // Assert that all elements in the vector are alive
33//! tracker.all_alive(1..=3)
34//! .expect("expected all elements to be alive");
35//!
36//! // Once the vector is dropped, all items should be dropped with it
37//! drop(v);
38//! tracker.all_dropped(1..=3)
39//! .expect("expected all elements to be dropped");
40//! ```
41//!
42//! This is how you would test a struct that involves [`MaybeUninit`]:
43//!
44//! ```should_panic
45//! # #![allow(dead_code)]
46//! use std::mem::MaybeUninit;
47//!
48//! struct MyOption<T> {
49//! set: bool,
50//! data: MaybeUninit<T>,
51//! }
52//!
53//! impl<T> MyOption<T> {
54//! fn none() -> Self {
55//! Self { set: false, data: MaybeUninit::uninit() }
56//! }
57//!
58//! fn some(x: T) -> Self {
59//! Self { set: true, data: MaybeUninit::new(x) }
60//! }
61//! }
62//!
63//! // BUG: MyOption<T> does not implement Drop!
64//! // BUG: The instance inside `data` may be initialized but not be properly destructed!
65//!
66//! // BUG: The following code will silently leak memory:
67//! let opt = MyOption::some(String::from("hello"));
68//! drop(opt); // the String does not get deallocated
69//!
70//! // DropTracker is able to catch this sort of bugs:
71//! use drop_tracker::DropTracker;
72//!
73//! let mut tracker = DropTracker::new();
74//! let opt = MyOption::some(tracker.track("item"));
75//!
76//! tracker.state(&"item")
77//! .alive()
78//! .expect("item is expected to be alive"); // works
79//!
80//! drop(opt);
81//!
82//! tracker.state(&"item")
83//! .dropped()
84//! .expect("item is expected to be dropped"); // panics, meaning that the bug was detected
85//! ```
86//!
87//! If you want to write more succint code and don't care about the error message, you can also use
88//! the following `assert` methods:
89//!
90//! * [`assert_alive(...)`](DropTracker::assert_alive) equivalent to
91//! `state(...).alive().expect("error message")`
92//! * [`assert_dropped(...)`](DropTracker::assert_dropped) equivalent to
93//! `state(...).dropped().expect("error message")`
94//! * [`assert_all_alive(...)`](DropTracker::assert_all_alive) equivalent to
95//! `all_alive(...).expect("error message")`
96//! * [`assert_all_dropped(...)`](DropTracker::assert_all_dropped) equivalent to
97//! `all_dropped(...).expect("error message")`
98//!
99//! Here is how the first example above could be rewritten more consisely using `assert` methods:
100//!
101//! ```
102//! use drop_tracker::DropTracker;
103//!
104//! let mut tracker = DropTracker::new();
105//! let v = vec![tracker.track(1),
106//! tracker.track(2),
107//! tracker.track(3)];
108//!
109//! tracker.assert_all_alive(1..=3);
110//! drop(v);
111//! tracker.assert_all_dropped(1..=3);
112//! ```
113//!
114//! # Double drop
115//!
116//! [`DropItem`] will panic if it gets dropped twice or more, as this is generally a bug and may
117//! cause undefined behavior. This feature can be used to identify bugs with code using
118//! [`ManuallyDrop`](std::mem::ManuallyDrop), [`MaybeUninit`](std::mem::MaybeUninit) or
119//! [`std::ptr::drop_in_place`], like in the following example:
120//!
121//! ```should_panic
122//! use std::ptr;
123//! use drop_tracker::DropTracker;
124//!
125//! let mut tracker = DropTracker::new();
126//! let mut item = tracker.track("something");
127//!
128//! unsafe { ptr::drop_in_place(&mut item); } // ok
129//! unsafe { ptr::drop_in_place(&mut item); } // panic!
130//! ```
131//!
132//! # Use in collections
133//!
134//! The [`DropItem`] instances returned by [`DropTracker::track`] hold a clone of the key passed
135//! to `track`. The `DropItem`s are [comparable](std::cmp) and [hashable](std::hash) if the
136//! underlying key is. This makes `DropItem` instances usable directly in collections like
137//! [`HashMap`](std::collections::HashMap), [`BTreeMap`](std::collections::BTreeMap),
138//! [`HashSet`](std::collections::HashSet) and many more.
139//!
140//! Here is an example involving [`HashSet`](std::collections::HashSet):
141//!
142//! ```
143//! use drop_tracker::DropTracker;
144//! use std::collections::HashSet;
145//!
146//! let mut tracker = DropTracker::new();
147//!
148//! let mut set = HashSet::from([
149//! tracker.track(1),
150//! tracker.track(2),
151//! tracker.track(3),
152//! ]);
153//!
154//! set.remove(&3);
155//!
156//! tracker.state(&1).alive().expect("first item should be alive");
157//! tracker.state(&2).alive().expect("second item should be alive");
158//! tracker.state(&3).dropped().expect("third item should be dropped");
159//! ```
160//!
161//! Keys are required to be hashable and unique. If you need [`DropItem`] to hold a non-hashable
162//! value, or a repeated value, you can construct a [`DropItem`] with an arbitrary value using
163//! [`DropTracker::track_with_value`]:
164//!
165//! ```
166//! use drop_tracker::DropTracker;
167//!
168//! let mut tracker = DropTracker::new();
169//!
170//! // Construct items identified by integers and holding floats (which are not hashable)
171//! let item1 = tracker.track_with_value(1, 7.52);
172//! let item2 = tracker.track_with_value(2, 3.89);
173//!
174//! // Items compare according to their value
175//! assert!(item1 > item2); // 7.52 > 3.89
176//!
177//! // Items that support comparison can be put in a vector and sorted
178//! let mut v = vec![item1, item2];
179//! v.sort_by(|x, y| x.partial_cmp(y).unwrap());
180//! ```
181
182#![warn(missing_debug_implementations)]
183#![warn(missing_docs)]
184#![warn(unreachable_pub)]
185#![warn(unused_crate_dependencies)]
186#![warn(unused_qualifications)]
187#![doc(test(attr(deny(warnings))))]
188
189#[cfg(test)]
190mod tests;
191
192mod itemtraits;
193
194use std::borrow::Borrow;
195use std::collections::HashMap;
196use std::collections::hash_map::Entry;
197use std::error::Error;
198use std::fmt;
199use std::hash::Hash;
200use std::iter::FusedIterator;
201use std::mem::MaybeUninit;
202use std::sync::Arc;
203use std::sync::atomic::AtomicBool;
204use std::sync::atomic::Ordering;
205
206/// A type that represents the state of a [`DropItem`]: either alive or dropped.
207///
208/// See the [module documentation](self) for details.
209#[must_use = "you should check whether the status is alive or dropped"]
210#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
211pub enum State {
212 /// The item is alive.
213 Alive,
214 /// The item has been dropped, and its destructor has been called.
215 Dropped,
216}
217
218impl State {
219 /// Returns `true` if the state is [`Alive`](State::Alive).
220 ///
221 /// # Examples
222 ///
223 /// ```
224 /// use drop_tracker::State;
225 ///
226 /// assert!(State::Alive.is_alive());
227 /// assert!(!State::Dropped.is_alive());
228 /// ```
229 #[inline]
230 #[must_use = "if you intended to assert that this is alive, consider `.alive().expect()`"]
231 pub const fn is_alive(&self) -> bool {
232 match self {
233 Self::Alive => true,
234 Self::Dropped => false,
235 }
236 }
237
238 /// Returns `true` if the state is [`Dropped`](State::Dropped).
239 ///
240 /// # Examples
241 ///
242 /// ```
243 /// use drop_tracker::State;
244 ///
245 /// assert!(State::Dropped.is_dropped());
246 /// assert!(!State::Alive.is_dropped());
247 /// ```
248 #[inline]
249 #[must_use = "if you intended to assert that this is dropped, consider `.dropped().expect()`"]
250 pub const fn is_dropped(&self) -> bool {
251 match self {
252 Self::Alive => false,
253 Self::Dropped => true,
254 }
255 }
256
257 /// Returns [`Ok`] if the state is [`Alive`](State::Alive), [`Err`] otherwise.
258 ///
259 /// # Examples
260 ///
261 /// ```
262 /// use drop_tracker::DroppedError;
263 /// use drop_tracker::State;
264 ///
265 /// assert_eq!(State::Alive.alive(), Ok(()));
266 /// assert_eq!(State::Dropped.alive(), Err(DroppedError));
267 /// ```
268 #[inline]
269 #[must_use = "if you intended to assert that this is alive, consider `.alive().expect()`"]
270 pub const fn alive(&self) -> Result<(), DroppedError> {
271 match self {
272 Self::Alive => Ok(()),
273 Self::Dropped => Err(DroppedError),
274 }
275 }
276
277 /// Returns [`Ok`] if the state is [`Dropped`](State::Dropped), [`Err`] otherwise.
278 ///
279 /// # Examples
280 ///
281 /// ```
282 /// use drop_tracker::AliveError;
283 /// use drop_tracker::State;
284 ///
285 /// assert_eq!(State::Dropped.dropped(), Ok(()));
286 /// assert_eq!(State::Alive.dropped(), Err(AliveError));
287 /// ```
288 #[inline]
289 #[must_use = "if you intended to assert that this is dropped, consider `.dropped().expect()`"]
290 pub const fn dropped(&self) -> Result<(), AliveError> {
291 match self {
292 Self::Alive => Err(AliveError),
293 Self::Dropped => Ok(()),
294 }
295 }
296}
297
298// Uses an `AtomicBool` (as opposed to e.g. a `RefCell`) to ensure that `DropTracker` and
299// `DropItem` are `Send`, `Sync` and `UnwindSafe`.
300#[derive(Clone, Debug)]
301struct StateCell(Arc<AtomicBool>);
302
303impl StateCell {
304 #[inline]
305 #[must_use]
306 fn new(state: State) -> Self {
307 Self(Arc::new(AtomicBool::new(state.is_dropped())))
308 }
309
310 #[inline]
311 fn get(&self) -> State {
312 match self.0.load(Ordering::Relaxed) {
313 false => State::Alive,
314 true => State::Dropped,
315 }
316 }
317
318 #[inline]
319 fn replace(&mut self, state: State) -> State {
320 match self.0.swap(state.is_dropped(), Ordering::Relaxed) {
321 false => State::Alive,
322 true => State::Dropped,
323 }
324 }
325
326 #[inline]
327 #[must_use]
328 fn is_alive(&self) -> bool {
329 self.get().is_alive()
330 }
331
332 #[inline]
333 #[must_use]
334 fn is_dropped(&self) -> bool {
335 self.get().is_dropped()
336 }
337}
338
339/// Creates [`DropItem`]s and tracks their state.
340///
341/// [`DropItem`]s can be created using [`track`](DropTracker::track) or
342/// [`try_track`](DropTracker::try_track) and their state can be later checked using
343/// [`state`](DropTracker::state).
344///
345/// [`DropItem`]s are identified by keys. A key can be of any type that implement the [`Hash`]
346/// and [`Eq`] traits, which include, for example: [`u32`], [`char`], [`str`], ...
347///
348/// See the [module documentation](self) for details.
349#[derive(Default, Debug)]
350pub struct DropTracker<K> {
351 tracked: HashMap<K, StateCell>,
352}
353
354impl<K> DropTracker<K> {
355 /// Creates a new empty `DropTracker`.
356 ///
357 /// # Examples
358 ///
359 /// ```
360 /// use drop_tracker::DropTracker;
361 ///
362 /// let tracker = DropTracker::<u32>::new();
363 /// assert_eq!(tracker.tracked().count(), 0);
364 /// ```
365 #[must_use]
366 pub fn new() -> Self {
367 Self {
368 tracked: HashMap::new(),
369 }
370 }
371
372 /// Returns an iterator over the keys tracked by this `DropTracker`.
373 ///
374 /// The order of keys returned by this iterator is non deterministic.
375 ///
376 /// # Examples
377 ///
378 /// ```
379 /// # #![allow(unused_variables)]
380 /// use drop_tracker::DropTracker;
381 ///
382 /// let mut tracker = DropTracker::new();
383 /// let item_a = tracker.track("a");
384 /// let item_b = tracker.track("b");
385 /// let item_c = tracker.track("c");
386 ///
387 /// let mut keys = tracker.tracked()
388 /// .collect::<Vec<&&str>>();
389 /// keys.sort();
390 /// assert_eq!(keys, [&"a", &"b", &"c"]);
391 /// ```
392 pub fn tracked(&self) -> impl Clone + ExactSizeIterator<Item = &K> + FusedIterator {
393 self.tracked.keys()
394 }
395
396 /// Returns an iterator over the keys tracked by this `DropTracker` that are alive.
397 ///
398 /// The order of keys returned by this iterator is non deterministic.
399 ///
400 /// # Examples
401 ///
402 /// ```
403 /// use drop_tracker::DropTracker;
404 ///
405 /// let mut tracker = DropTracker::new();
406 /// let item_a = tracker.track("a");
407 /// let item_b = tracker.track("b");
408 /// let item_c = tracker.track("c");
409 ///
410 /// drop(item_c);
411 ///
412 /// let mut alive_keys = tracker.alive()
413 /// .collect::<Vec<&&str>>();
414 /// alive_keys.sort();
415 /// assert_eq!(alive_keys, [&"a", &"b"]);
416 ///
417 /// drop(item_a);
418 /// drop(item_b);
419 ///
420 /// assert_eq!(tracker.alive().count(), 0);
421 /// ```
422 pub fn alive(&self) -> impl Clone + FusedIterator<Item = &K> {
423 self.tracked
424 .iter()
425 .filter(|(_, state)| state.is_alive())
426 .map(|(key, _)| key)
427 }
428
429 /// Returns an iterator over the keys tracked by this `DropTracker` that have been dropped.
430 ///
431 /// The order of keys returned by this iterator is non deterministic.
432 ///
433 /// # Examples
434 ///
435 /// ```
436 /// # #![allow(unused_variables)]
437 /// use drop_tracker::DropTracker;
438 ///
439 /// let mut tracker = DropTracker::new();
440 /// let item_a = tracker.track("a");
441 /// let item_b = tracker.track("b");
442 /// let item_c = tracker.track("c");
443 ///
444 /// assert_eq!(tracker.dropped().count(), 0);
445 ///
446 /// drop(item_a);
447 /// drop(item_b);
448 ///
449 /// let mut alive_keys = tracker.dropped()
450 /// .collect::<Vec<&&str>>();
451 /// alive_keys.sort();
452 /// assert_eq!(alive_keys, [&"a", &"b"]);
453 /// ```
454 pub fn dropped(&self) -> impl Clone + FusedIterator<Item = &K> {
455 self.tracked
456 .iter()
457 .filter(|(_, state)| state.is_dropped())
458 .map(|(key, _)| key)
459 }
460
461 /// Forgets all the items tracked by this `DropTracker`.
462 ///
463 /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
464 /// longer be possible to query their status after forgetting them.
465 ///
466 /// # Examples
467 ///
468 /// ```
469 /// # #![allow(unused_variables)]
470 /// use drop_tracker::DropTracker;
471 ///
472 /// let mut tracker = DropTracker::new();
473 /// assert_eq!(tracker.tracked().count(), 0);
474 ///
475 /// let item_a = tracker.track("a");
476 /// let item_b = tracker.track("b");
477 /// let item_c = tracker.track("c");
478 /// assert_eq!(tracker.tracked().count(), 3);
479 ///
480 /// tracker.forget_all();
481 /// assert_eq!(tracker.tracked().count(), 0);
482 /// ```
483 pub fn forget_all(&mut self) {
484 self.tracked.clear();
485 }
486
487 /// Forgets all the items tracked by this `DropTracker` that have been dropped.
488 ///
489 /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
490 /// longer be possible to query their status after forgetting them.
491 ///
492 /// # Examples
493 ///
494 /// ```
495 /// # #![allow(unused_variables)]
496 /// use drop_tracker::DropTracker;
497 ///
498 /// let mut tracker = DropTracker::new();
499 /// assert_eq!(tracker.tracked().count(), 0);
500 ///
501 /// let item_a = tracker.track("a");
502 /// let item_b = tracker.track("b");
503 /// let item_c = tracker.track("c");
504 /// assert_eq!(tracker.tracked().count(), 3);
505 ///
506 /// // After dropping an item, the item is still tracked
507 /// drop(item_a);
508 /// drop(item_b);
509 /// assert_eq!(tracker.tracked().count(), 3);
510 ///
511 /// // Use `forget_dropped` to lose track of items that have been dropped
512 /// tracker.forget_dropped();
513 /// assert_eq!(tracker.tracked().count(), 1);
514 ///
515 /// let mut keys = tracker.tracked()
516 /// .collect::<Vec<&&str>>();
517 /// keys.sort();
518 /// assert_eq!(keys, [&"c"]);
519 /// ```
520 pub fn forget_dropped(&mut self) {
521 self.tracked.retain(|_, state| state.is_alive())
522 }
523}
524
525impl<K: Hash + Eq> DropTracker<K> {
526 /// Creates a new [`DropItem`] identified by the given key.
527 ///
528 /// The value held by the `DropItem` is a clone of the key. Use
529 /// [`DropTracker::track_with_value`] if you wish to specify a custom value.
530 ///
531 /// # Panics
532 ///
533 /// Panics if the key is already used by another tracked item.
534 ///
535 /// Call [`forget`](DropTracker::forget),
536 /// [`forget_dropped`](DropTracker::forget_dropped) or
537 /// [`forget_all`](DropTracker::forget_all) if you wish to reuse a key from an item you no
538 /// longer need to track.
539 ///
540 /// See [`try_track`](DropTracker::try_track) for a variant of this method that does not panic.
541 ///
542 /// # Examples
543 ///
544 /// ```
545 /// use drop_tracker::DropTracker;
546 /// use drop_tracker::State;
547 ///
548 /// let mut tracker = DropTracker::new();
549 ///
550 /// let item = tracker.track("abc");
551 /// assert_eq!(tracker.state("abc"), State::Alive);
552 ///
553 /// drop(item);
554 /// assert_eq!(tracker.state("abc"), State::Dropped);
555 /// ```
556 ///
557 /// Using the same key twice causes a panic:
558 ///
559 /// ```should_panic
560 /// # #![allow(unused_variables)]
561 /// use drop_tracker::DropTracker;
562 ///
563 /// let mut tracker = DropTracker::new();
564 ///
565 /// let item1 = tracker.track("abc");
566 /// let item2 = tracker.track("abc"); // panics!
567 /// ```
568 ///
569 /// Use [`forget`](DropTracker::forget) to reuse the same key:
570 ///
571 /// ```
572 /// # #![allow(unused_variables)]
573 /// use drop_tracker::DropTracker;
574 ///
575 /// let mut tracker = DropTracker::new();
576 ///
577 /// let item1 = tracker.track("abc");
578 /// let _ = tracker.forget("abc");
579 /// let item2 = tracker.track("abc"); // works
580 /// ```
581 pub fn track(&mut self, key: K) -> DropItem<K>
582 where
583 K: Clone,
584 {
585 self.try_track(key).expect("cannot track key")
586 }
587
588 /// Creates a new [`DropItem`] identified by the given key, or [`Err`] if the key is
589 /// already in use.
590 ///
591 /// The value held by the `DropItem` is a clone of the key. Use
592 /// [`DropTracker::try_track_with_value`] if you wish to specify a custom value.
593 ///
594 /// See also [`track`](DropTracker::track).
595 ///
596 /// # Examples
597 ///
598 /// ```
599 /// # #![allow(unused_variables)]
600 /// use drop_tracker::DropTracker;
601 ///
602 /// let mut tracker = DropTracker::new();
603 ///
604 /// let item = tracker.try_track("abc");
605 /// assert!(item.is_ok());
606 ///
607 /// let item = tracker.try_track("abc");
608 /// assert!(item.is_err()); // key is already used
609 /// ```
610 pub fn try_track(&mut self, key: K) -> Result<DropItem<K>, CollisionError>
611 where
612 K: Clone,
613 {
614 let value = key.clone();
615 self.try_track_with_value(key, value)
616 }
617
618 /// Creates a new [`DropItem`] identified by the given key and holding the given value.
619 ///
620 /// # Panics
621 ///
622 /// Panics if the key is already used by another tracked item.
623 ///
624 /// Call [`forget`](DropTracker::forget),
625 /// [`forget_dropped`](DropTracker::forget_dropped) or
626 /// [`forget_all`](DropTracker::forget_all) if you wish to reuse a key from an item you no
627 /// longer need to track.
628 ///
629 /// See [`try_track_with_value`](DropTracker::try_track_with_value) for a variant of this
630 /// method that does not panic.
631 ///
632 /// # Examples
633 ///
634 /// ```
635 /// use drop_tracker::DropTracker;
636 /// use drop_tracker::State;
637 ///
638 /// let mut tracker = DropTracker::new();
639 ///
640 /// let item = tracker.track_with_value("abc", vec![1, 2, 3]);
641 /// assert_eq!(tracker.state("abc"), State::Alive);
642 ///
643 /// drop(item);
644 /// assert_eq!(tracker.state("abc"), State::Dropped);
645 /// ```
646 ///
647 /// Using the same key twice causes a panic:
648 ///
649 /// ```should_panic
650 /// # #![allow(unused_variables)]
651 /// use drop_tracker::DropTracker;
652 ///
653 /// let mut tracker = DropTracker::new();
654 ///
655 /// let item1 = tracker.track_with_value("abc", vec![1, 2, 3]);
656 /// let item2 = tracker.track_with_value("abc", vec![4, 5, 6]); // panics!
657 /// ```
658 ///
659 /// Use [`forget`](DropTracker::forget) to reuse the same key:
660 ///
661 /// ```
662 /// # #![allow(unused_variables)]
663 /// use drop_tracker::DropTracker;
664 ///
665 /// let mut tracker = DropTracker::new();
666 ///
667 /// let item1 = tracker.track_with_value("abc", vec![1, 2, 3]);
668 /// let _ = tracker.forget("abc");
669 /// let item2 = tracker.track_with_value("abc", vec![4, 5, 6]); // works
670 /// ```
671 pub fn track_with_value<V>(&mut self, key: K, value: V) -> DropItem<V> {
672 self.try_track_with_value(key, value)
673 .expect("cannot track key")
674 }
675
676 /// Creates a new [`DropItem`] identified by the given key and holding the given value, or
677 /// [`Err`] if the key is already in use.
678 ///
679 /// See also [`track_with_value`](DropTracker::track_with_value).
680 ///
681 /// # Examples
682 ///
683 /// ```
684 /// # #![allow(unused_variables)]
685 /// use drop_tracker::DropTracker;
686 ///
687 /// let mut tracker = DropTracker::new();
688 ///
689 /// let item = tracker.try_track_with_value("abc", vec![1, 2, 3]);
690 /// assert!(item.is_ok());
691 ///
692 /// let item = tracker.try_track_with_value("abc", vec![4, 5, 6]);
693 /// assert!(item.is_err()); // key is already used
694 /// ```
695 pub fn try_track_with_value<V>(
696 &mut self,
697 key: K,
698 value: V,
699 ) -> Result<DropItem<V>, CollisionError> {
700 let state = StateCell::new(State::Alive);
701 match self.tracked.entry(key) {
702 Entry::Occupied(_) => Err(CollisionError),
703 Entry::Vacant(entry) => {
704 entry.insert(state.clone());
705 Ok(DropItem::new(value, state))
706 }
707 }
708 }
709
710 /// Creates multiple new [`DropItem`] structs, each identified by a key from the given
711 /// iterable.
712 ///
713 /// Calling `track_many` is equivalent to calling [`track`](DropTracker::track) multiple times.
714 ///
715 /// Note: this method returns an iterator that must be fully consumed in order for the items to
716 /// be tracked. Discarding the return value would cause items to be ignored and not be tracked.
717 ///
718 /// # Examples
719 ///
720 /// ```
721 /// use drop_tracker::DropTracker;
722 /// use drop_tracker::State;
723 ///
724 /// let mut tracker = DropTracker::new();
725 ///
726 /// let mut items = tracker.track_many(["abc", "def", "ghi"]);
727 ///
728 /// let abc = items.next().unwrap();
729 /// let def = items.next().unwrap();
730 /// let ghi = items.next().unwrap();
731 /// assert_eq!(items.next(), None);
732 /// drop(items);
733 ///
734 /// assert_eq!(abc, "abc");
735 /// assert_eq!(def, "def");
736 /// assert_eq!(ghi, "ghi");
737 ///
738 /// assert_eq!(tracker.state("abc"), State::Alive);
739 /// assert_eq!(tracker.state("def"), State::Alive);
740 /// assert_eq!(tracker.state("ghi"), State::Alive);
741 ///
742 /// drop(def);
743 ///
744 /// assert_eq!(tracker.state("abc"), State::Alive);
745 /// assert_eq!(tracker.state("def"), State::Dropped);
746 /// assert_eq!(tracker.state("ghi"), State::Alive);
747 ///
748 /// # drop(abc);
749 /// # drop(ghi);
750 /// ```
751 pub fn track_many<'a, Iter>(&'a mut self, keys: Iter) -> impl Iterator<Item = DropItem<K>> + 'a
752 where
753 Iter: IntoIterator<Item = K> + 'a,
754 K: Clone,
755 {
756 keys.into_iter().map(|key| self.track(key))
757 }
758}
759
760impl<K: Hash + Eq> DropTracker<K> {
761 /// Checks the state of a [`DropItem`] tracked by this `DropTracker`: [alive](State::Alive) or
762 /// [dropped](State::Dropped).
763 ///
764 /// # Panics
765 ///
766 /// Panics if the given key is not tracked.
767 ///
768 /// See [`try_state`](DropTracker::try_state) for a variant of this method that does not panic.
769 ///
770 /// # Examples
771 ///
772 /// ```
773 /// use drop_tracker::DropTracker;
774 /// use drop_tracker::State;
775 ///
776 /// let mut tracker = DropTracker::new();
777 ///
778 /// let item = tracker.track("abc");
779 /// assert_eq!(tracker.state("abc"), State::Alive);
780 ///
781 /// drop(item);
782 /// assert_eq!(tracker.state("abc"), State::Dropped);
783 /// ```
784 ///
785 /// Querying a key that is not tracked causes a panic:
786 ///
787 /// ```should_panic
788 /// # #![allow(unused_variables)]
789 /// use drop_tracker::DropTracker;
790 ///
791 /// let mut tracker = DropTracker::new();
792 ///
793 /// let item = tracker.track("abc");
794 /// let state = tracker.state("def"); // panics!
795 /// ```
796 pub fn state<Q>(&self, key: &Q) -> State
797 where
798 K: Borrow<Q>,
799 Q: Hash + Eq + ?Sized,
800 {
801 self.try_state(key).expect("cannot get state")
802 }
803
804 /// Checks the state of a [`DropItem`] tracked by this `DropTracker`: [alive](State::Alive) or
805 /// [dropped](State::Dropped). Returns [`Err`] it the given key is not tracked.
806 ///
807 /// See also [`state`](DropTracker::state).
808 ///
809 /// # Examples
810 ///
811 /// ```
812 /// use drop_tracker::DropTracker;
813 /// use drop_tracker::NotTrackedError;
814 /// use drop_tracker::State;
815 ///
816 /// let mut tracker = DropTracker::new();
817 ///
818 /// let item = tracker.track("abc");
819 /// assert_eq!(tracker.try_state("abc"), Ok(State::Alive));
820 /// assert_eq!(tracker.try_state("def"), Err(NotTrackedError));
821 ///
822 /// drop(item);
823 /// assert_eq!(tracker.try_state("abc"), Ok(State::Dropped));
824 /// assert_eq!(tracker.try_state("def"), Err(NotTrackedError));
825 /// ```
826 pub fn try_state<Q>(&self, key: &Q) -> Result<State, NotTrackedError>
827 where
828 K: Borrow<Q>,
829 Q: Hash + Eq + ?Sized,
830 {
831 self.tracked
832 .get(key)
833 .ok_or(NotTrackedError)
834 .map(|state| state.get())
835 }
836
837 /// Forgets an item tracked by this `DropTracker`, and returns its current state
838 /// ([alive](State::Alive) or [dropped](State::Dropped)).
839 ///
840 /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
841 /// longer be possible to query their status after forgetting them.
842 ///
843 /// # Panics
844 ///
845 /// Panics if the given key is not tracked.
846 ///
847 /// See [`try_forget`](DropTracker::try_forget) for a variant of this method that does not panic.
848 ///
849 /// # Examples
850 ///
851 /// ```
852 /// # #![allow(unused_variables)]
853 /// use drop_tracker::DropTracker;
854 /// use drop_tracker::State;
855 ///
856 /// let mut tracker = DropTracker::new();
857 ///
858 /// let item = tracker.track("a");
859 /// assert!(tracker.is_tracked("a"));
860 ///
861 /// assert_eq!(tracker.forget("a"), State::Alive);
862 /// assert!(!tracker.is_tracked("a"));
863 /// ```
864 ///
865 /// Forgetting a key that is not tracked causes a panic:
866 ///
867 /// ```should_panic
868 /// # #![allow(unused_variables)]
869 /// use drop_tracker::DropTracker;
870 ///
871 /// let mut tracker = DropTracker::new();
872 ///
873 /// let item = tracker.track("abc");
874 /// let state = tracker.forget("def"); // panics!
875 /// ```
876 pub fn forget<Q>(&mut self, key: &Q) -> State
877 where
878 K: Borrow<Q>,
879 Q: Hash + Eq + ?Sized,
880 {
881 self.try_forget(key).expect("cannot forget item")
882 }
883
884 /// Forgets an item tracked by this `DropTracker`, and returns its current state
885 /// ([alive](State::Alive) or [dropped](State::Dropped)), or [`Err`] if the item is not
886 /// tracked.
887 ///
888 /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
889 /// longer be possible to query their status after forgetting them.
890 ///
891 /// See also [`forget`](DropTracker::forget).
892 ///
893 /// # Examples
894 ///
895 /// ```
896 /// # #![allow(unused_variables)]
897 /// use drop_tracker::DropTracker;
898 /// use drop_tracker::NotTrackedError;
899 /// use drop_tracker::State;
900 ///
901 /// let mut tracker = DropTracker::new();
902 ///
903 /// let item = tracker.track("a");
904 /// assert!(tracker.is_tracked("a"));
905 ///
906 /// assert_eq!(tracker.try_forget("a"), Ok(State::Alive));
907 /// assert_eq!(tracker.try_forget("b"), Err(NotTrackedError));
908 /// ```
909 pub fn try_forget<Q>(&mut self, key: &Q) -> Result<State, NotTrackedError>
910 where
911 K: Borrow<Q>,
912 Q: Hash + Eq + ?Sized,
913 {
914 self.tracked
915 .remove(key)
916 .ok_or(NotTrackedError)
917 .map(|state| state.get())
918 }
919
920 /// Returns [`true`] if an item identified by the given key is tracked by this `DropTracker`,
921 /// [`false`] otherwise.
922 ///
923 /// # Examples
924 ///
925 /// ```
926 /// # #![allow(unused_variables)]
927 /// use drop_tracker::DropTracker;
928 ///
929 /// let mut tracker = DropTracker::new();
930 /// assert!(!tracker.is_tracked("abc"));
931 ///
932 /// let item = tracker.track("abc");
933 /// assert!(tracker.is_tracked("abc"));
934 /// ```
935 #[must_use]
936 pub fn is_tracked<Q>(&self, key: &Q) -> bool
937 where
938 K: Borrow<Q>,
939 Q: Hash + Eq + ?Sized,
940 {
941 self.try_state(key).is_ok()
942 }
943
944 /// Returns [`Ok`] if all the given keys point to items that are [alive](State::Alive),
945 /// [`Err`] otherwise.
946 ///
947 /// An error may be returned in two cases: either a key is not tracked, or it has been dropped.
948 ///
949 /// This method returns `Ok` if the sequence of keys passed is empty.
950 ///
951 /// # Examples
952 ///
953 /// ```
954 /// # #![allow(unused_variables)]
955 /// use drop_tracker::DropTracker;
956 /// use drop_tracker::NotAllAliveError;
957 ///
958 /// let mut tracker = DropTracker::new();
959 ///
960 /// let item1 = tracker.track(1);
961 /// let item2 = tracker.track(2);
962 /// let item3 = tracker.track(3);
963 /// let item4 = tracker.track(4);
964 ///
965 /// drop(item3);
966 /// drop(item4);
967 ///
968 /// assert_eq!(tracker.all_alive([1, 2]), Ok(()));
969 ///
970 /// assert_eq!(tracker.all_alive([1, 2, 3, 4, 5, 6]),
971 /// Err(NotAllAliveError {
972 /// dropped: vec![3, 4],
973 /// untracked: vec![5, 6],
974 /// }));
975 /// ```
976 ///
977 /// Passing an empty set of keys returns `Ok`:
978 ///
979 /// ```
980 /// use drop_tracker::DropTracker;
981 ///
982 /// let tracker = DropTracker::<()>::new();
983 /// assert_eq!(tracker.all_alive([(); 0]), Ok(()));
984 /// ```
985 pub fn all_alive<Q, Item, Iter>(&self, iter: Iter) -> Result<(), NotAllAliveError<Item>>
986 where
987 K: Borrow<Q>,
988 Q: Hash + Eq + ?Sized,
989 Item: Borrow<Q>,
990 Iter: IntoIterator<Item = Item>,
991 {
992 // Vec won't allocate any memory until items are pushed to it, so if this method does not
993 // fail, no memory will be allocated
994 let mut err = NotAllAliveError {
995 dropped: Vec::new(),
996 untracked: Vec::new(),
997 };
998
999 for key in iter {
1000 match self.try_state(key.borrow()) {
1001 Ok(State::Alive) => (),
1002 Ok(State::Dropped) => err.dropped.push(key),
1003 Err(NotTrackedError) => err.untracked.push(key),
1004 }
1005 }
1006
1007 if err.dropped.is_empty() && err.untracked.is_empty() {
1008 Ok(())
1009 } else {
1010 Err(err)
1011 }
1012 }
1013
1014 /// Returns [`Ok`] if all the given keys point to items that are [dropped](State::Dropped),
1015 /// [`Err`] otherwise.
1016 ///
1017 /// An error may be returned in two cases: either a key is not tracked, or it is alive.
1018 ///
1019 /// This method returns `Ok` if the sequence of keys passed is empty.
1020 ///
1021 /// # Examples
1022 ///
1023 /// ```
1024 /// # #![allow(unused_variables)]
1025 /// use drop_tracker::DropTracker;
1026 /// use drop_tracker::NotAllDroppedError;
1027 ///
1028 /// let mut tracker = DropTracker::new();
1029 ///
1030 /// let item1 = tracker.track(1);
1031 /// let item2 = tracker.track(2);
1032 /// let item3 = tracker.track(3);
1033 /// let item4 = tracker.track(4);
1034 ///
1035 /// drop(item3);
1036 /// drop(item4);
1037 ///
1038 /// assert_eq!(tracker.all_dropped([3, 4]), Ok(()));
1039 ///
1040 /// assert_eq!(tracker.all_dropped([1, 2, 3, 4, 5, 6]),
1041 /// Err(NotAllDroppedError {
1042 /// alive: vec![1, 2],
1043 /// untracked: vec![5, 6],
1044 /// }));
1045 /// ```
1046 ///
1047 /// Passing an empty set of keys returns `Ok`:
1048 ///
1049 /// ```
1050 /// use drop_tracker::DropTracker;
1051 ///
1052 /// let tracker = DropTracker::<()>::new();
1053 /// assert_eq!(tracker.all_dropped([(); 0]), Ok(()));
1054 /// ```
1055 pub fn all_dropped<Q, Item, Iter>(&self, iter: Iter) -> Result<(), NotAllDroppedError<Item>>
1056 where
1057 K: Borrow<Q>,
1058 Q: Hash + Eq + ?Sized,
1059 Item: Borrow<Q>,
1060 Iter: IntoIterator<Item = Item>,
1061 {
1062 // Vec won't allocate any memory until items are pushed to it, so if this method does not
1063 // fail, no memory will be allocated
1064 let mut err = NotAllDroppedError {
1065 alive: Vec::new(),
1066 untracked: Vec::new(),
1067 };
1068
1069 for key in iter {
1070 match self.try_state(key.borrow()) {
1071 Ok(State::Alive) => err.alive.push(key),
1072 Ok(State::Dropped) => (),
1073 Err(NotTrackedError) => err.untracked.push(key),
1074 }
1075 }
1076
1077 if err.alive.is_empty() && err.untracked.is_empty() {
1078 Ok(())
1079 } else {
1080 Err(err)
1081 }
1082 }
1083
1084 /// Returns [`Ok`] if all the keys tracked are [alive](State::Alive), [`Err`] otherwise.
1085 ///
1086 /// The error returned references an arbitrary keys that was found [dropped](State::Dropped).
1087 ///
1088 /// If the tracker is empty, this method returns `Ok`.
1089 ///
1090 /// # Examples
1091 ///
1092 /// ```
1093 /// # #![allow(unused_variables)]
1094 /// use drop_tracker::DropTracker;
1095 /// use drop_tracker::SomeDroppedError;
1096 ///
1097 /// let mut tracker = DropTracker::new();
1098 ///
1099 /// let item1 = tracker.track(1);
1100 /// let item2 = tracker.track(2);
1101 /// let item3 = tracker.track(3);
1102 ///
1103 /// assert_eq!(tracker.fully_alive(), Ok(()));
1104 ///
1105 /// drop(item1);
1106 ///
1107 /// assert_eq!(tracker.fully_alive(), Err(SomeDroppedError { dropped: &1 }));
1108 /// ```
1109 ///
1110 /// Calling `fully_alive()` on an empty tracker always returns `Ok`:
1111 ///
1112 /// ```
1113 /// use drop_tracker::DropTracker;
1114 ///
1115 /// let tracker = DropTracker::<()>::new();
1116 /// assert_eq!(tracker.fully_alive(), Ok(()));
1117 /// ```
1118 pub fn fully_alive(&self) -> Result<(), SomeDroppedError<'_, K>> {
1119 let dropped = self
1120 .tracked
1121 .iter()
1122 .find(|(_, state)| state.is_dropped())
1123 .map(|(key, _)| key);
1124 match dropped {
1125 None => Ok(()),
1126 Some(dropped) => Err(SomeDroppedError { dropped }),
1127 }
1128 }
1129
1130 /// Returns [`Ok`] if all the keys tracked are [dropped](State::Dropped), [`Err`] otherwise.
1131 ///
1132 /// The error returned references an arbitrary keys that was found [alive](State::Alive).
1133 ///
1134 /// If the tracker is empty, this method returns `Ok`.
1135 ///
1136 /// # Examples
1137 ///
1138 /// ```
1139 /// # #![allow(unused_variables)]
1140 /// use drop_tracker::DropTracker;
1141 /// use drop_tracker::SomeAliveError;
1142 ///
1143 /// let mut tracker = DropTracker::new();
1144 ///
1145 /// let item1 = tracker.track(1);
1146 /// let item2 = tracker.track(2);
1147 /// let item3 = tracker.track(3);
1148 ///
1149 /// drop(item1);
1150 /// drop(item2);
1151 ///
1152 /// assert_eq!(tracker.fully_dropped(), Err(SomeAliveError { alive: &3 }));
1153 ///
1154 /// drop(item3);
1155 ///
1156 /// assert_eq!(tracker.fully_dropped(), Ok(()));
1157 /// ```
1158 ///
1159 /// Calling `fully_dropped()` on an empty tracker always returns `Ok`:
1160 ///
1161 /// ```
1162 /// use drop_tracker::DropTracker;
1163 ///
1164 /// let tracker = DropTracker::<()>::new();
1165 /// assert_eq!(tracker.fully_dropped(), Ok(()));
1166 /// ```
1167 pub fn fully_dropped(&self) -> Result<(), SomeAliveError<'_, K>> {
1168 let alive = self
1169 .tracked
1170 .iter()
1171 .find(|(_, state)| state.is_alive())
1172 .map(|(key, _)| key);
1173 match alive {
1174 None => Ok(()),
1175 Some(alive) => Err(SomeAliveError { alive }),
1176 }
1177 }
1178
1179 /// Checks that all the given key points to an item that is [alive](State::Alive), panics
1180 /// otherwise.
1181 ///
1182 /// `assert_alive(...)` is a shortcut for `state(...).alive().unwrap()`. See
1183 /// [`state()`](DropTracker::state) for more details.
1184 ///
1185 /// # Panics
1186 ///
1187 /// If the key is not tracked, or if it has been dropped.
1188 ///
1189 /// # Examples
1190 ///
1191 /// ```should_panic
1192 /// # #![allow(unused_variables)]
1193 /// use drop_tracker::DropTracker;
1194 ///
1195 /// let mut tracker = DropTracker::new();
1196 ///
1197 /// let item2 = tracker.track(1);
1198 /// let item2 = tracker.track(2);
1199 ///
1200 /// drop(item2);
1201 ///
1202 /// tracker.assert_alive(&1); // succeeds
1203 /// tracker.assert_alive(&2); // panics (item was dropped)
1204 /// tracker.assert_alive(&3); // panics (key is not tracked)
1205 /// ```
1206 pub fn assert_alive<Q>(&self, key: &Q)
1207 where
1208 K: Borrow<Q>,
1209 Q: Hash + Eq + ?Sized,
1210 {
1211 let state = match self.try_state(key) {
1212 Ok(state) => state,
1213 Err(err) => panic!("{err}"),
1214 };
1215 match state.alive() {
1216 Ok(()) => (),
1217 Err(err) => panic!("{err}"),
1218 }
1219 }
1220
1221 /// Checks that all the given key points to an item that is [dropped](State::Dropped), panics
1222 /// otherwise.
1223 ///
1224 /// `assert_dropped(...)` is a shortcut for `state(...).dropped().unwrap()`. See
1225 /// [`state()`](DropTracker::state) for more details.
1226 ///
1227 /// # Panics
1228 ///
1229 /// If the key is not tracked, or if it is alive.
1230 ///
1231 /// # Examples
1232 ///
1233 /// ```should_panic
1234 /// # #![allow(unused_variables)]
1235 /// use drop_tracker::DropTracker;
1236 ///
1237 /// let mut tracker = DropTracker::new();
1238 ///
1239 /// let item1 = tracker.track(1);
1240 /// let item2 = tracker.track(2);
1241 ///
1242 /// drop(item1);
1243 ///
1244 /// tracker.assert_dropped(&1); // succeeds
1245 /// tracker.assert_dropped(&2); // panics (item is alive)
1246 /// tracker.assert_dropped(&3); // panics (key is not tracked)
1247 /// ```
1248 pub fn assert_dropped<Q>(&self, key: &Q)
1249 where
1250 K: Borrow<Q>,
1251 Q: Hash + Eq + ?Sized,
1252 {
1253 let state = match self.try_state(key) {
1254 Ok(state) => state,
1255 Err(err) => panic!("{err}"),
1256 };
1257 match state.dropped() {
1258 Ok(()) => (),
1259 Err(err) => panic!("{err}"),
1260 }
1261 }
1262
1263 /// Checks that all the given keys point to items that are [alive](State::Alive), panics
1264 /// otherwise.
1265 ///
1266 /// `assert_all_alive(...)` is a shortcut for `all_alive(...).unwrap()`. See
1267 /// [`all_alive()`](DropTracker::all_alive) for more details.
1268 ///
1269 /// # Panics
1270 ///
1271 /// If a key is not tracked, or if it has been dropped.
1272 ///
1273 /// # Examples
1274 ///
1275 /// ```should_panic
1276 /// # #![allow(unused_variables)]
1277 /// use drop_tracker::DropTracker;
1278 ///
1279 /// let mut tracker = DropTracker::new();
1280 ///
1281 /// let item1 = tracker.track(1);
1282 /// let item2 = tracker.track(2);
1283 /// let item3 = tracker.track(3);
1284 /// let item4 = tracker.track(4);
1285 ///
1286 /// drop(item3);
1287 /// drop(item4);
1288 ///
1289 /// tracker.assert_all_alive([1, 2]); // succeeds
1290 /// tracker.assert_all_alive([3, 4]); // panics (items were dropped)
1291 /// tracker.assert_all_alive([5, 6]); // panics (keys are not tracked)
1292 /// ```
1293 ///
1294 /// Passing an empty set of keys succeeds:
1295 ///
1296 /// ```
1297 /// use drop_tracker::DropTracker;
1298 ///
1299 /// let tracker = DropTracker::<()>::new();
1300 /// tracker.assert_all_alive([(); 0]);
1301 /// ```
1302 pub fn assert_all_alive<Q, Item, Iter>(&self, iter: Iter)
1303 where
1304 K: Borrow<Q>,
1305 Q: Hash + Eq + ?Sized,
1306 Item: Borrow<Q> + fmt::Debug,
1307 Iter: IntoIterator<Item = Item>,
1308 {
1309 match self.all_alive(iter) {
1310 Ok(()) => (),
1311 Err(err) => panic!("{err}"),
1312 }
1313 }
1314
1315 /// Checks that all the given keys point to items that are [dropped](State::Dropped), panics
1316 /// otherwise.
1317 ///
1318 /// `assert_all_dropped(...)` is a shortcut for `all_dropped(...).unwrap()`. See
1319 /// [`all_dropped()`](DropTracker::all_dropped) for more details.
1320 ///
1321 /// # Panics
1322 ///
1323 /// If a key is not tracked, or if it is alive.
1324 ///
1325 /// # Examples
1326 ///
1327 /// ```should_panic
1328 /// # #![allow(unused_variables)]
1329 /// use drop_tracker::DropTracker;
1330 ///
1331 /// let mut tracker = DropTracker::new();
1332 ///
1333 /// let item1 = tracker.track(1);
1334 /// let item2 = tracker.track(2);
1335 /// let item3 = tracker.track(3);
1336 /// let item4 = tracker.track(4);
1337 ///
1338 /// drop(item1);
1339 /// drop(item2);
1340 ///
1341 /// tracker.assert_all_dropped([1, 2]); // succeeds
1342 /// tracker.assert_all_dropped([3, 4]); // panics (items are alive)
1343 /// tracker.assert_all_dropped([5, 6]); // panics (keys are not tracked)
1344 /// ```
1345 ///
1346 /// Passing an empty set of keys succeeds:
1347 ///
1348 /// ```
1349 /// use drop_tracker::DropTracker;
1350 ///
1351 /// let tracker = DropTracker::<()>::new();
1352 /// tracker.assert_all_dropped([(); 0]);
1353 /// ```
1354 pub fn assert_all_dropped<Q, Item, Iter>(&self, iter: Iter)
1355 where
1356 K: Borrow<Q>,
1357 Q: Hash + Eq + ?Sized,
1358 Item: Borrow<Q> + fmt::Debug,
1359 Iter: IntoIterator<Item = Item>,
1360 {
1361 match self.all_dropped(iter) {
1362 Ok(()) => (),
1363 Err(err) => panic!("{err}"),
1364 }
1365 }
1366
1367 /// Checks that all the keys tracked are [alive](State::Alive), panics otherwise.
1368 ///
1369 /// `assert_fully_alive()` is a shortcut for `fully_alive().unwrap()`. See
1370 /// [`fully_alive()`](DropTracker::fully_alive) for more details.
1371 ///
1372 /// # Panics
1373 ///
1374 /// If one or more items were found to have been [dropped](State::Dropped).
1375 ///
1376 /// # Examples
1377 ///
1378 /// Calling `assert_fully_alive()` when all items are alive succeeds:
1379 ///
1380 /// ```
1381 /// # #![allow(unused_variables)]
1382 /// use drop_tracker::DropTracker;
1383 ///
1384 /// let mut tracker = DropTracker::new();
1385 ///
1386 /// let item1 = tracker.track(1);
1387 /// let item2 = tracker.track(2);
1388 ///
1389 /// tracker.assert_fully_alive(); // succeeds
1390 /// ```
1391 ///
1392 /// Calling `assert_fully_alive()` when one or more items are dropped causes a panic:
1393 ///
1394 /// ```should_panic
1395 /// # #![allow(unused_variables)]
1396 /// use drop_tracker::DropTracker;
1397 ///
1398 /// let mut tracker = DropTracker::new();
1399 ///
1400 /// let item1 = tracker.track(1);
1401 /// let item2 = tracker.track(2);
1402 ///
1403 /// drop(item1);
1404 ///
1405 /// tracker.assert_fully_alive(); // panics
1406 /// ```
1407 ///
1408 /// Calling `assert_fully_alive()` when the tracker is empty succeeds:
1409 ///
1410 /// ```
1411 /// use drop_tracker::DropTracker;
1412 ///
1413 /// let tracker = DropTracker::<()>::new();
1414 /// tracker.assert_fully_alive(); // succeeds
1415 /// ```
1416 pub fn assert_fully_alive(&self)
1417 where
1418 K: fmt::Debug,
1419 {
1420 match self.fully_alive() {
1421 Ok(()) => (),
1422 Err(err) => panic!("{err}"),
1423 }
1424 }
1425
1426 /// Checks that all the keys tracked are [dropped](State::Dropped), panics otherwise.
1427 ///
1428 /// `assert_fully_dropped()` is a shortcut for `fully_dropped().unwrap()`. See
1429 /// [`fully_dropped()`](DropTracker::fully_dropped) for more details.
1430 ///
1431 /// # Panics
1432 ///
1433 /// If one or more items were found [alive](State::Alive).
1434 ///
1435 /// # Examples
1436 ///
1437 /// Calling `assert_fully_dropped()` when items are alive causes a panic:
1438 ///
1439 /// ```should_panic
1440 /// # #![allow(unused_variables)]
1441 /// use drop_tracker::DropTracker;
1442 ///
1443 /// let mut tracker = DropTracker::new();
1444 ///
1445 /// let item1 = tracker.track(1);
1446 /// let item2 = tracker.track(2);
1447 ///
1448 /// tracker.assert_fully_dropped(); // panics
1449 /// ```
1450 ///
1451 /// Calling `assert_fully_dropped()` when all items are dropped succeeds:
1452 ///
1453 /// ```
1454 /// use drop_tracker::DropTracker;
1455 ///
1456 /// let mut tracker = DropTracker::new();
1457 ///
1458 /// let item1 = tracker.track(1);
1459 /// let item2 = tracker.track(2);
1460 ///
1461 /// drop(item1);
1462 /// drop(item2);
1463 ///
1464 /// tracker.assert_fully_dropped(); // succeeds
1465 /// ```
1466 ///
1467 /// Calling `assert_fully_dropped()` when the tracker is empty succeeds:
1468 ///
1469 /// ```
1470 /// use drop_tracker::DropTracker;
1471 ///
1472 /// let tracker = DropTracker::<()>::new();
1473 /// tracker.assert_fully_dropped(); // succeeds
1474 /// ```
1475 pub fn assert_fully_dropped(&self)
1476 where
1477 K: fmt::Debug,
1478 {
1479 match self.fully_dropped() {
1480 Ok(()) => (),
1481 Err(err) => panic!("{err}"),
1482 }
1483 }
1484}
1485
1486/// An item that will notify the parent [`DropTracker`] once it gets dropped.
1487///
1488/// `DropItem` instances are created by [`DropTracker::track`], [`DropTracker::track_with_value`],
1489/// and related functions. `DropItem` instances may contain an "underlying value" that affects the
1490/// item behavior when used with standard traits. The underlying value is either:
1491///
1492/// * a clone of `key` when constructing an item using `track(key)` (implicit); or
1493/// * `value` when constructing an item using `track_with_value(key, value)` (explicit).
1494///
1495/// To check whether an item is alive or has been dropped, use [`DropTracker::state`] or see the
1496/// documentation for [`DropTracker`] for alternatives.
1497///
1498/// # Coercing and borrowing
1499///
1500/// `DropItem` instances may be [_coerced_](std::ops::Deref) and [_borrowed_](std::borrow::Borrow)
1501/// as the the underlying value type. This means that, for example, if you create a `DropItem`
1502/// using `track(String::from("abc"))`, you may call all of the `String` methods on that item.
1503///
1504/// `DropItem` also implements the standard traits [`PartialEq`](std::cmp::PartialEq),
1505/// [`Eq`](std::cmp::Eq), [`PartialOrd`](std::cmp::PartialOrd), [`Ord`](std::cmp::Ord) and
1506/// [`Hash`](std::hash::Hash), [`Display`](std::fmt::Display), [`Debug`](std::fmt::Debug) if the
1507/// type of the underlying value implements them.
1508///
1509/// # Cloning
1510///
1511/// `DropItem` does not implement the [`Clone`] trait as it would introduce ambiguity with respect
1512/// to understanding whether the item has been dropped or is still alive when using
1513/// [`DropTracker::state`].
1514///
1515/// # Double drop
1516///
1517/// `DropItem` instances can be dropped twice or more. Doing so will cause a panic, but will not
1518/// cause undefined behavior (unless you're calling drop on an invalid memory location). The panic
1519/// on double drop is an useful feature to detect logic errors in destructors.
1520///
1521/// # Safety
1522///
1523/// Borrowing or performing operations on the underlying value of a `DropItem` is generally safe
1524/// when using safe Rust code. However, `DropItem`s are often used in unsafe code and are used to
1525/// detect potential bugs. In those circumstances, it is possible to trigger undefined behavior.
1526/// In particular, borrowing or performing operations on a `DropItem` while another thread is
1527/// dropping will result in undefined behavior (although it must be noted that this is a bug in the
1528/// caller code and is not something that should happen in safe Rust code).
1529///
1530/// Only [`Drop`](std::ops::Drop) on a `DropItem` is guaranteed to be safe in all circumstances.
1531///
1532/// # Examples
1533///
1534/// ```
1535/// use drop_tracker::DropTracker;
1536///
1537/// let mut tracker = DropTracker::<u32>::new();
1538///
1539/// // Create an item using `123u32` as the key. Implicitly, this also sets its value to `123u32`
1540/// let item = tracker.track(123);
1541///
1542/// // Check that the item is alive
1543/// tracker.state(&123).alive().expect("item should be alive");
1544///
1545/// // Dereference the value of the item
1546/// assert_eq!(*item, 123);
1547/// assert!(!item.is_power_of_two());
1548///
1549/// // Drop the item and check that it really got dropped
1550/// drop(item);
1551/// tracker.state(&123).dropped().expect("item should be dropped");
1552///
1553/// // Create a new item, this time using an explicit `String` value
1554/// let abc_item = tracker.track_with_value(111, String::from("abc"));
1555///
1556/// // Comparison with other items using `String` work using the underlying `String`
1557/// // operations
1558/// assert_eq!(abc_item, tracker.track_with_value(222, String::from("abc")));
1559/// assert_ne!(abc_item, tracker.track_with_value(333, String::from("def")));
1560/// assert!(abc_item < tracker.track_with_value(444, String::from("def")));
1561///
1562/// // Display, debug and hashing also work using the underlying `String` operations
1563/// assert_eq!(format!("{}", abc_item), "abc");
1564/// assert_eq!(format!("{:?}", abc_item), "DropItem { value: \"abc\", state: Alive }");
1565///
1566/// use std::collections::hash_map::DefaultHasher;
1567/// use std::hash::Hash;
1568/// use std::hash::Hasher;
1569/// fn hash<T: Hash + ?Sized>(x: &T) -> u64 {
1570/// let mut hasher = DefaultHasher::new();
1571/// x.hash(&mut hasher);
1572/// hasher.finish()
1573/// }
1574/// assert_eq!(hash(&abc_item), hash(&"abc"));
1575///
1576/// // Methods on `String` can be called transparently on items
1577/// assert_eq!(abc_item.to_ascii_uppercase(), "ABC");
1578/// ```
1579///
1580/// Using hashable items in a set, with an implicit underlying value:
1581///
1582/// ```
1583/// use drop_tracker::DropTracker;
1584/// use std::collections::HashSet;
1585///
1586/// let mut tracker = DropTracker::new();
1587///
1588/// let mut set = HashSet::from([
1589/// tracker.track(1),
1590/// tracker.track(2),
1591/// tracker.track(3),
1592/// ]);
1593///
1594/// set.remove(&3);
1595///
1596/// tracker.state(&1).alive().expect("first item should be alive");
1597/// tracker.state(&2).alive().expect("second item should be alive");
1598/// tracker.state(&3).dropped().expect("third item should be dropped");
1599/// ```
1600///
1601/// Using hashable items in a set, with an explicit underlying value:
1602///
1603/// ```
1604/// use drop_tracker::DropTracker;
1605/// use std::collections::HashSet;
1606///
1607/// let mut tracker = DropTracker::new();
1608///
1609/// let mut set = HashSet::from([
1610/// tracker.track_with_value(1, String::from("first")),
1611/// tracker.track_with_value(2, String::from("second")),
1612/// tracker.track_with_value(3, String::from("third")),
1613/// ]);
1614///
1615/// set.remove("third");
1616///
1617/// tracker.state(&1).alive().expect("first item should be alive");
1618/// tracker.state(&2).alive().expect("second item should be alive");
1619/// tracker.state(&3).dropped().expect("third item should be dropped");
1620/// ```
1621#[must_use = "if you don't use this item, it will get automatically dropped"]
1622pub struct DropItem<V> {
1623 value: MaybeUninit<V>,
1624 state: Option<StateCell>,
1625}
1626
1627impl<V> DropItem<V> {
1628 const fn new(value: V, state: StateCell) -> Self {
1629 Self {
1630 value: MaybeUninit::new(value),
1631 state: Some(state),
1632 }
1633 }
1634}
1635
1636impl<V> Drop for DropItem<V> {
1637 fn drop(&mut self) {
1638 // The use of an Option might seem redundant, but it's actually needed to safely detect and
1639 // report double drops. Without the Option, we would be touching shared memory behind an Rc
1640 // that probably does not exist anymore, causing memory corruption. The Option makes this a
1641 // bit safer (assuming that the DropItem memory has not been moved or altered), and also
1642 // prevents a double drop on the Rc.
1643 match self.state.take() {
1644 Some(mut state) => {
1645 if state.replace(State::Dropped).is_dropped() {
1646 panic!("item dropped twice");
1647 }
1648 // SAFETY: `state` was `Some(State::Alive)`, which means that `value` has not been
1649 // dropped yet and that `value` is initialized.
1650 unsafe { self.value.assume_init_drop() };
1651 }
1652 None => {
1653 panic!("item dropped twice");
1654 }
1655 }
1656 }
1657}
1658
1659/// Error signaling that an item was expected to have been dropped, but it's [alive](State::Alive).
1660///
1661/// See [`State::dropped`] for more information and examples.
1662#[derive(PartialEq, Eq, Debug)]
1663pub struct AliveError;
1664
1665impl fmt::Display for AliveError {
1666 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1667 fmt::Display::fmt("item is alive", f)
1668 }
1669}
1670
1671impl Error for AliveError {}
1672
1673/// Error signaling that an item was expected to be alive, but it was [dropped](State::Dropped).
1674///
1675/// See [`State::alive`] for more information and examples.
1676#[derive(PartialEq, Eq, Debug)]
1677pub struct DroppedError;
1678
1679impl fmt::Display for DroppedError {
1680 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1681 fmt::Display::fmt("item is dropped", f)
1682 }
1683}
1684
1685impl Error for DroppedError {}
1686
1687/// Error returned when trying to place multiple items with the same key inside the same [`DropTracker`].
1688///
1689/// See [`DropTracker::try_track`] for more information and examples.
1690#[derive(PartialEq, Eq, Debug)]
1691pub struct CollisionError;
1692
1693impl fmt::Display for CollisionError {
1694 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1695 fmt::Display::fmt("another item with the same key is already tracked", f)
1696 }
1697}
1698
1699impl Error for CollisionError {}
1700
1701/// Error returned when failing to query the status of an item with a key that is not known to [`DropTracker`].
1702///
1703/// See [`DropTracker::try_state`] for more information and examples.
1704#[derive(PartialEq, Eq, Debug)]
1705pub struct NotTrackedError;
1706
1707impl fmt::Display for NotTrackedError {
1708 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1709 fmt::Display::fmt("item is not tracked", f)
1710 }
1711}
1712
1713impl Error for NotTrackedError {}
1714
1715/// Error returned when failing to assert that a set of items is all [alive](State::Alive).
1716///
1717/// See [`DropTracker::all_alive`] for more information and examples.
1718#[derive(PartialEq, Eq, Debug)]
1719pub struct NotAllAliveError<K> {
1720 /// Sequence of keys that were expected to be alive, but were dropped.
1721 pub dropped: Vec<K>,
1722 /// Sequence of keys that were expected to be alive, but are not tracked by the
1723 /// [`DropTracker`].
1724 pub untracked: Vec<K>,
1725}
1726
1727impl<K: fmt::Debug> fmt::Display for NotAllAliveError<K> {
1728 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1729 write!(f, "not all items are alive: ")?;
1730 if !self.dropped.is_empty() {
1731 write!(f, "dropped: {:?}", &self.dropped)?;
1732 }
1733 if !self.untracked.is_empty() {
1734 if !self.dropped.is_empty() {
1735 write!(f, ", ")?;
1736 }
1737 write!(f, "not tracked: {:?}", &self.untracked)?;
1738 }
1739 Ok(())
1740 }
1741}
1742
1743impl<K: fmt::Debug> Error for NotAllAliveError<K> {}
1744
1745/// Error returned when failing to assert that a set of items is all [dropped](State::Dropped).
1746///
1747/// See [`DropTracker::all_dropped`] for more information and examples.
1748#[derive(PartialEq, Eq, Debug)]
1749pub struct NotAllDroppedError<K> {
1750 /// Sequence of keys that were expected to be dropped, but are alive.
1751 pub alive: Vec<K>,
1752 /// Sequence of keys that were expected to be dropped, but are not tracked by the
1753 /// [`DropTracker`].
1754 pub untracked: Vec<K>,
1755}
1756
1757impl<K: fmt::Debug> fmt::Display for NotAllDroppedError<K> {
1758 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1759 write!(f, "not all items are dropped: ")?;
1760 if !self.alive.is_empty() {
1761 write!(f, "alive: {:?}", &self.alive)?;
1762 }
1763 if !self.untracked.is_empty() {
1764 if !self.alive.is_empty() {
1765 write!(f, ", ")?;
1766 }
1767 write!(f, "not tracked: {:?}", &self.untracked)?;
1768 }
1769 Ok(())
1770 }
1771}
1772
1773impl<K: fmt::Debug> Error for NotAllDroppedError<K> {}
1774
1775/// Error returned when failing to assert that all tracked items are [dropped](State::Dropped).
1776///
1777/// See [`DropTracker::fully_dropped`] for more information and examples.
1778#[derive(PartialEq, Eq, Debug)]
1779pub struct SomeAliveError<'a, K> {
1780 /// Reference to the first key that was found [alive](State::Alive).
1781 pub alive: &'a K,
1782}
1783
1784impl<'a, K: fmt::Debug> fmt::Display for SomeAliveError<'a, K> {
1785 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1786 write!(f, "item is alive: {:?}", self.alive)
1787 }
1788}
1789
1790impl<'a, K: fmt::Debug> Error for SomeAliveError<'a, K> {}
1791
1792/// Error returned when failing to assert that all tracked items are [alive](State::Alive).
1793///
1794/// See [`DropTracker::fully_alive`] for more information and examples.
1795#[derive(PartialEq, Eq, Debug)]
1796pub struct SomeDroppedError<'a, K> {
1797 /// Reference to the first key that was found to have been be [dropped](State::Dropped).
1798 pub dropped: &'a K,
1799}
1800
1801impl<'a, K: fmt::Debug> fmt::Display for SomeDroppedError<'a, K> {
1802 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1803 write!(f, "item is dropped: {:?}", self.dropped)
1804 }
1805}
1806
1807impl<'a, K: fmt::Debug> Error for SomeDroppedError<'a, K> {}