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