shadow_shim_helper_rs/
explicit_drop.rs

1/// Trait for a type that provides an explicit method for dropping its value.
2///
3/// Unlike the `Drop` trait, this traits method:
4/// * Can take a parameter
5/// * Can return something
6/// * Consumes the value (instead of taking a `&mut`)
7///
8/// Unfortunately there is no built-in way to *ensure* a type is explicitly
9/// dropped. One workaround is for a type to also implement `Drop`, and have
10/// that implementation validate that `ExplicitDrop::explicit_drop` was called.
11pub trait ExplicitDrop {
12    type ExplicitDropParam;
13    type ExplicitDropResult;
14    fn explicit_drop(self, param: &Self::ExplicitDropParam) -> Self::ExplicitDropResult;
15}
16
17/// Wrapper that uses a provided function to drop the inner value.
18///
19/// This is helpful for working with locals that must be explicitly dropped, without
20/// having to explicitly do so at every potential exit point of the current function.
21///
22/// ```
23/// # use shadow_shim_helper_rs::explicit_drop::{ExplicitDrop, ExplicitDropper};
24/// # use shadow_shim_helper_rs::rootedcell::Root;
25/// # use shadow_shim_helper_rs::rootedcell::rc::RootedRc;
26/// # use std::string::String;
27/// # use std::error::Error;
28/// fn validate_and_rc(root: &Root, s: String) -> Result<RootedRc<String>, Box<dyn Error>> {
29///   let rc_string = ExplicitDropper::new(
30///     RootedRc::new(root, s),
31///     |value| value.explicit_drop(root));
32///
33///   if !rc_string.starts_with("x") {
34///     // `ExplicitDropper` will call the provided closure, safely dropping the RootedRc.
35///     return Err("bad prefix".into());
36///   }
37///
38///   // We extract the value from the dropper at the point where we transfer ownership;
39///   // in this case the closure is never called.
40///   return Ok(rc_string.into_value())
41/// }
42/// ```
43pub struct ExplicitDropper<DropFn, Value>
44where
45    DropFn: FnOnce(Value),
46{
47    internal: Option<(DropFn, Value)>,
48}
49
50impl<DropFn, Value> ExplicitDropper<DropFn, Value>
51where
52    DropFn: FnOnce(Value),
53{
54    /// Create a wrapped value
55    pub fn new(value: Value, dropper: DropFn) -> Self {
56        Self {
57            internal: Some((dropper, value)),
58        }
59    }
60
61    /// Unwrap the value, discarding the dropper.
62    pub fn into_value(mut self) -> Value {
63        let (_drop_fn, value) = self.internal.take().unwrap();
64        value
65    }
66}
67
68impl<DropFn, Value> Drop for ExplicitDropper<DropFn, Value>
69where
70    DropFn: FnOnce(Value),
71{
72    fn drop(&mut self) {
73        if let Some((drop_fn, value)) = self.internal.take() {
74            drop_fn(value)
75        }
76    }
77}
78
79impl<DropFn, Value> std::ops::Deref for ExplicitDropper<DropFn, Value>
80where
81    DropFn: FnOnce(Value),
82{
83    type Target = Value;
84
85    fn deref(&self) -> &Self::Target {
86        let (_drop_fn, value) = self.internal.as_ref().unwrap();
87        value
88    }
89}
90
91impl<DropFn, Value> std::ops::DerefMut for ExplicitDropper<DropFn, Value>
92where
93    DropFn: FnOnce(Value),
94{
95    fn deref_mut(&mut self) -> &mut Self::Target {
96        let (_drop_fn, value) = self.internal.as_mut().unwrap();
97        value
98    }
99}
100
101#[cfg(test)]
102mod test {
103    use super::*;
104
105    #[test]
106    fn test_explicit_dropper_explicitly_drops() {
107        let mut dropper_called = false;
108        let x = ExplicitDropper::new(42, |val| {
109            assert_eq!(val, 42);
110            dropper_called = true;
111        });
112        drop(x);
113        assert!(dropper_called);
114    }
115
116    #[test]
117    fn test_explicit_dropper_into_value() {
118        let mut dropper_called = false;
119        let x = ExplicitDropper::new(42, |_| dropper_called = true);
120        assert_eq!(x.into_value(), 42);
121        assert!(!dropper_called);
122    }
123}