aboutsummaryrefslogtreecommitdiff
path: root/src/util/maybe_dangling.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/maybe_dangling.rs')
-rw-r--r--src/util/maybe_dangling.rs67
1 files changed, 67 insertions, 0 deletions
diff --git a/src/util/maybe_dangling.rs b/src/util/maybe_dangling.rs
new file mode 100644
index 0000000..c29a089
--- /dev/null
+++ b/src/util/maybe_dangling.rs
@@ -0,0 +1,67 @@
+use core::future::Future;
+use core::mem::MaybeUninit;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+/// A wrapper type that tells the compiler that the contents might not be valid.
+///
+/// This is necessary mainly when `T` contains a reference. In that case, the
+/// compiler will sometimes assume that the reference is always valid; in some
+/// cases it will assume this even after the destructor of `T` runs. For
+/// example, when a reference is used as a function argument, then the compiler
+/// will assume that the reference is valid until the function returns, even if
+/// the reference is destroyed during the function. When the reference is used
+/// as part of a self-referential struct, that assumption can be false. Wrapping
+/// the reference in this type prevents the compiler from making that
+/// assumption.
+///
+/// # Invariants
+///
+/// The `MaybeUninit` will always contain a valid value until the destructor runs.
+//
+// Reference
+// See <https://users.rust-lang.org/t/unsafe-code-review-semi-owning-weak-rwlock-t-guard/95706>
+//
+// TODO: replace this with an official solution once RFC #3336 or similar is available.
+// <https://github.com/rust-lang/rfcs/pull/3336>
+#[repr(transparent)]
+pub(crate) struct MaybeDangling<T>(MaybeUninit<T>);
+
+impl<T> Drop for MaybeDangling<T> {
+ fn drop(&mut self) {
+ // Safety: `0` is always initialized.
+ unsafe { core::ptr::drop_in_place(self.0.as_mut_ptr()) };
+ }
+}
+
+impl<T> MaybeDangling<T> {
+ pub(crate) fn new(inner: T) -> Self {
+ Self(MaybeUninit::new(inner))
+ }
+}
+
+impl<F: Future> Future for MaybeDangling<F> {
+ type Output = F::Output;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ // Safety: `0` is always initialized.
+ let fut = unsafe { self.map_unchecked_mut(|this| this.0.assume_init_mut()) };
+ fut.poll(cx)
+ }
+}
+
+#[test]
+fn maybedangling_runs_drop() {
+ struct SetOnDrop<'a>(&'a mut bool);
+
+ impl Drop for SetOnDrop<'_> {
+ fn drop(&mut self) {
+ *self.0 = true;
+ }
+ }
+
+ let mut success = false;
+
+ drop(MaybeDangling::new(SetOnDrop(&mut success)));
+ assert!(success);
+}