68 lines
2.1 KiB
Rust
68 lines
2.1 KiB
Rust
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);
|
|
}
|