104 lines
2.7 KiB
Rust
104 lines
2.7 KiB
Rust
//! Generic RAII wrappers that execute actions when the guard is dropped in the style of Zig's
|
|
//! [`defer`](https://ziglang.org/documentation/master/#defer) statement.
|
|
//!
|
|
//! Take this rather contrived example:
|
|
//! ```
|
|
//! use std::cell::RefCell;
|
|
//! use drop_guard::defer;
|
|
//!
|
|
//! let s = RefCell::new(String::new());
|
|
//! {
|
|
//! let _guard = defer(|| s.borrow_mut().push_str("World"));
|
|
//! let _guard = defer(|| s.borrow_mut().push_str(", "));
|
|
//! s.borrow_mut().push_str("Hello");
|
|
//! }
|
|
//!
|
|
//! assert_eq!(s.into_inner(), "Hello, World");
|
|
//! ```
|
|
|
|
pub(crate) mod macros;
|
|
|
|
pub mod ext;
|
|
|
|
use std::mem::ManuallyDrop;
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
/// Defers execution of the provided [`DropFn`] until the returned [`DropGuard`] is dropped.
|
|
pub const fn defer<F: DropFn<Data = ()>>(f: F) -> DropGuard<F> {
|
|
DropGuard::new(f)
|
|
}
|
|
|
|
/// Defers execution of the provided [`DropFn`], with data `d`, until the returned [`DropGuard`] is dropped.
|
|
pub const fn defer_with_data<F: DropFn>(f: F, d: F::Data) -> DropGuard<F> {
|
|
DropGuard::with_data(f, d)
|
|
}
|
|
|
|
pub trait DropFn {
|
|
// TODO: Use a default of `()` once the associated_type_defaults (rust-lang/rust#29661) feature lands
|
|
type Data;
|
|
|
|
fn on_drop(self, data: Self::Data);
|
|
}
|
|
|
|
impl<F: FnOnce() -> ()> DropFn for F {
|
|
type Data = ();
|
|
|
|
fn on_drop(self, _: ()) {
|
|
self()
|
|
}
|
|
}
|
|
|
|
pub struct DropGuard<F: DropFn>(ManuallyDrop<F>, ManuallyDrop<F::Data>);
|
|
|
|
impl<F: DropFn> DropGuard<F> {
|
|
/// Same as [`defer_with_data`].
|
|
pub const fn with_data(f: F, d: F::Data) -> Self {
|
|
Self(ManuallyDrop::new(f), ManuallyDrop::new(d))
|
|
}
|
|
|
|
pub fn unguard(slot: Self) -> F::Data {
|
|
Self::into_inner(slot).1
|
|
}
|
|
|
|
pub fn into_inner(slot: Self) -> (F, F::Data) {
|
|
let mut slot = ManuallyDrop::new(slot);
|
|
// SAFETY: the DropGuard (slot) is wrapped in a ManuallyDrop, so its Drop impl will not be run
|
|
unsafe {
|
|
(
|
|
ManuallyDrop::take(&mut slot.0),
|
|
ManuallyDrop::take(&mut slot.1),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<F: DropFn<Data = ()>> DropGuard<F> {
|
|
/// Same as [`defer`].
|
|
pub const fn new(f: F) -> Self {
|
|
Self::with_data(f, ())
|
|
}
|
|
}
|
|
|
|
impl<F: DropFn> Drop for DropGuard<F> {
|
|
fn drop(&mut self) {
|
|
// SAFETY: see guarantees on the Drop trait
|
|
let f = unsafe { ManuallyDrop::take(&mut self.0) };
|
|
let d = unsafe { ManuallyDrop::take(&mut self.1) };
|
|
f.on_drop(d);
|
|
}
|
|
}
|
|
|
|
impl<F: DropFn> Deref for DropGuard<F> {
|
|
type Target = F::Data;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.1
|
|
}
|
|
}
|
|
|
|
impl<F: DropFn> DerefMut for DropGuard<F> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.1
|
|
}
|
|
}
|