try_drop/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(drop_bounds)]
3#![allow(clippy::declare_interior_mutable_const)]
4#![warn(missing_docs)]
5#![no_std]
6
7#[cfg(feature = "shrinkwraprs")]
8#[macro_use]
9extern crate shrinkwraprs;
10
11#[cfg(feature = "std")]
12extern crate std;
13
14pub mod prelude;
15
16pub mod drop_strategies;
17
18mod infallible;
19
20pub use anyhow::Error;
21use core::sync::atomic::Ordering;
22pub use infallible::Infallible;
23
24#[cfg(any(feature = "global", feature = "thread-local"))]
25mod global_crate_root;
26
27#[cfg(any(feature = "global", feature = "thread-local"))]
28pub use global_crate_root::*;
29
30#[cfg(not(any(feature = "global", feature = "thread-local")))]
31pub use self::PureTryDrop as TryDrop;
32
33#[cfg(any(feature = "global", feature = "thread-local"))]
34pub use self::ImpureTryDrop as TryDrop;
35
36#[cfg(any(feature = "__tests", test))]
37pub mod test_utils;
38
39#[cfg(any(feature = "global", feature = "thread-local"))]
40pub mod handlers;
41
42pub mod adapters;
43
44use adapters::DropAdapter;
45
46#[allow(dead_code)]
47const LOAD_ORDERING: Ordering = Ordering::Acquire;
48
49#[allow(dead_code)]
50const STORE_ORDERING: Ordering = Ordering::Release;
51
52/// A trait for types which can be dropped, but which may fail to do so.
53///
54/// This is a pure version of try drop, meaning that the drop strategies have to be explicitly
55/// specified, which means it does not depend on a global try drop strategy.
56///
57/// # Gotchas
58/// Implementing this trait is not enough to make it droppable. In order for the try drop strategy
59/// to be run, you need to put your type in a [`DropAdapter`].
60///
61/// An easier way to make your type droppable is to call [`PureTryDrop::adapt`] on it.
62pub trait PureTryDrop {
63    /// The type of the error that may occur during drop.
64    type Error: Into<anyhow::Error>;
65
66    /// The type which will be used if the drop strategy fails.
67    type FallbackTryDropStrategy: TryDropStrategy;
68
69    /// The type which will be used if dropping fails.
70    type TryDropStrategy: FallibleTryDropStrategy;
71
72    /// Get a reference to the fallback try drop strategy.
73    fn fallback_try_drop_strategy(&self) -> &Self::FallbackTryDropStrategy;
74
75    /// Get a reference to the try drop strategy.
76    fn try_drop_strategy(&self) -> &Self::TryDropStrategy;
77
78    /// Adapts this type to take advantage of the specified try drop strategies.
79    ///
80    /// # Notes
81    /// If [`Self`] implements [`Copy`], and you call this function, at first it seems like there
82    /// would be a soundness hole:
83    ///
84    /// ```rust
85    /// use try_drop::{Infallible, PureTryDrop, TryDrop};
86    ///
87    /// #[derive(Copy, Clone)]
88    /// struct T(usize);
89    ///
90    /// impl TryDrop for T {
91    ///     type Error = Infallible;
92    ///
93    ///     unsafe fn try_drop(&mut self) -> Result<(), Self::Error> {
94    ///         self.0 += 1;
95    ///         println!("{}", self.0);
96    ///         Ok(())
97    ///     }
98    /// }
99    ///
100    /// // this is valid code and does not result in a compilation error
101    /// let t = T.adapt().adapt();
102    /// ```
103    ///
104    /// You'd think the output would be:
105    ///
106    /// ```ignore
107    /// 1
108    /// 2
109    /// ```
110    ///
111    /// However, it's actually:
112    ///
113    /// ```ignore
114    /// 1
115    /// 1
116    /// ```
117    ///
118    /// This is because [`Self`] implicitly get copied.
119    /// <sup><i>I may or may not have spent a large amount of time trying to get rid of this "soundness hole".</i></sup>
120    fn adapt(self) -> DropAdapter<Self>
121    where
122        Self: Sized,
123    {
124        DropAdapter(self)
125    }
126
127    /// Execute the fallible destructor for this type. This function is unsafe because if this is
128    /// called outside of a [`Drop::drop`] context, once the scope of the object implementing trait
129    /// ends, this function will be called twice, potentially resulting in a double-free.
130    ///
131    /// Use [`DropAdapter`] to ensure that the destructor is only called once.
132    ///
133    /// # Safety
134    /// The caller must ensure that this function is called within a [`Drop::drop`] context.
135    unsafe fn try_drop(&mut self) -> Result<(), Self::Error>;
136}
137
138/// A trait for types which can be dropped, but which may fail to do so.
139///
140/// This is an impure version of try drop, meaning that it depends on the global try drop strategy.
141///
142/// # Gotchas
143/// Implementing this trait is not enough to make it droppable. In order for the try drop strategy
144/// to be run, you need to put your type in a [`DropAdapter`].
145///
146/// An easier way to make your type droppable is to call [`PureTryDrop::adapt`] on it.
147#[cfg(any(feature = "global", feature = "thread-local"))]
148pub trait ImpureTryDrop {
149    /// The type of the error that may occur during drop.
150    type Error: Into<anyhow::Error>;
151
152    /// Execute the fallible destructor for this type. This function is unsafe because if this is
153    /// called outside of a [`Drop::drop`] context, once the scope of the object implementing trait
154    /// ends, this function will be called twice, potentially resulting in a double-free.
155    ///
156    /// Use [`DropAdapter`] to ensure that the destructor is only called once.
157    ///
158    /// # Safety
159    /// The caller must ensure that this function is called within a [`Drop::drop`] context.
160    ///
161    /// If the implementing type implements [`RepeatableTryDrop`], however, then this function is
162    /// safe to call multiple times. If the `unsafe` seems ugly to you, you can use
163    /// [`RepeatableTryDrop::safe_try_drop`].
164    unsafe fn try_drop(&mut self) -> Result<(), Self::Error>;
165}
166
167/// A trait which signifies a try drop strategy which can fail.
168pub trait FallibleTryDropStrategy {
169    /// The type of the error that may occur when handling a drop error.
170    type Error: Into<anyhow::Error>;
171
172    /// Try and handle a drop error.
173    fn try_handle_error(&self, error: anyhow::Error) -> Result<(), Self::Error>;
174}
175
176/// A trait which signifies a try drop strategy which can fail. Can be dynamically dispatched.
177pub trait DynFallibleTryDropStrategy {
178    /// Try to handle the drop error.
179    fn dyn_try_handle_error(&self, error: anyhow::Error) -> anyhow::Result<()>;
180}
181
182impl<T: FallibleTryDropStrategy> DynFallibleTryDropStrategy for T {
183    fn dyn_try_handle_error(&self, error: anyhow::Error) -> anyhow::Result<()> {
184        self.try_handle_error(error).map_err(Into::into)
185    }
186}
187
188/// A trait which signifies a try drop strategy which can fail, can be dynamically dispatched, and
189/// can be used as the global try drop strategy.
190#[cfg(feature = "global")]
191#[cfg(not(feature = "downcast-rs"))]
192pub trait GlobalDynFallibleTryDropStrategy: ThreadSafe + DynFallibleTryDropStrategy {}
193
194/// A trait which signifies a try drop strategy which can fail, can be dynamically dispatched, and
195/// can be used as the global try drop strategy.
196#[cfg(feature = "global")]
197#[cfg(feature = "downcast-rs")]
198pub trait GlobalDynFallibleTryDropStrategy:
199    ThreadSafe + downcast_rs::DowncastSync + DynFallibleTryDropStrategy
200{
201}
202
203#[cfg(feature = "global")]
204#[cfg(feature = "downcast-rs")]
205downcast_rs::impl_downcast!(sync GlobalDynFallibleTryDropStrategy);
206
207#[cfg(feature = "global")]
208impl<T: ThreadSafe + DynFallibleTryDropStrategy> GlobalDynFallibleTryDropStrategy for T {}
209
210/// A trait which signifies a try drop strategy which can be used in a thread local scenario. Must
211/// be dynamically dispatched and must live as long as the program does.
212#[cfg(feature = "thread-local")]
213pub trait ThreadLocalFallibleTryDropStrategy: DynFallibleTryDropStrategy + 'static {}
214
215#[cfg(feature = "thread-local")]
216impl<T: DynFallibleTryDropStrategy + 'static> ThreadLocalFallibleTryDropStrategy for T {}
217
218/// A trait which signifies a try drop strategy. This can never fail. If it can, use
219/// [`FallibleTryDropStrategy`] instead.
220pub trait TryDropStrategy {
221    /// Handle the drop error.
222    fn handle_error(&self, error: anyhow::Error);
223}
224
225impl<TDS: TryDropStrategy> FallibleTryDropStrategy for TDS {
226    type Error = Infallible;
227
228    fn try_handle_error(&self, error: anyhow::Error) -> Result<(), Self::Error> {
229        self.handle_error(error);
230        Ok(())
231    }
232}
233
234/// A trait which signifies a try drop strategy which can be used as the primary or fallback
235/// handler.
236#[cfg(feature = "global")]
237#[cfg(not(feature = "downcast-rs"))]
238pub trait GlobalTryDropStrategy: ThreadSafe + TryDropStrategy {}
239
240/// A trait which signifies a try drop strategy which can be used as the primary or fallback
241/// handler. Can be downcast.
242#[cfg(feature = "global")]
243#[cfg(feature = "downcast-rs")]
244pub trait GlobalTryDropStrategy: ThreadSafe + downcast_rs::DowncastSync + TryDropStrategy {}
245
246#[cfg(feature = "global")]
247#[cfg(feature = "downcast-rs")]
248downcast_rs::impl_downcast!(sync GlobalTryDropStrategy);
249
250#[cfg(feature = "global")]
251impl<T: ThreadSafe + TryDropStrategy> GlobalTryDropStrategy for T {}
252
253/// A trait which signifies an infallible try drop strategy which can be used in a thread local.
254#[cfg(feature = "thread-local")]
255pub trait ThreadLocalTryDropStrategy: TryDropStrategy + 'static {}
256
257#[cfg(feature = "thread-local")]
258impl<T: TryDropStrategy + 'static> ThreadLocalTryDropStrategy for T {}
259
260/// A trait which signifies a thread safe type. Can be used in a `static`.
261pub trait ThreadSafe: Send + Sync + 'static {}
262
263impl<T: Send + Sync + 'static> ThreadSafe for T {}
264
265/// Marker trait signifying that the implementing type can repeatedly call its [`TryDrop::try_drop`]
266/// method.
267///
268/// # Safety
269/// The implementor must ensure that no undefined behavior will occur when calling
270/// [`TryDrop::try_drop`] multiple times.
271pub unsafe trait RepeatableTryDrop: PureTryDrop {
272    /// Safely try and drop the implementing type. You can call this function multiple times.
273    fn safe_try_drop(&mut self) -> Result<(), Self::Error> {
274        // SAFETY: This is safe because the implementing type has implemented `RepeatableTryDrop`,
275        // which assures us that it is safe to call `try_drop` multiple times.
276        unsafe { self.try_drop() }
277    }
278}