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}