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}