1 | use core::future::Future; |
2 | use core::mem::MaybeUninit; |
3 | use core::pin::Pin; |
4 | use core::task::{Context, Poll}; |
5 | |
6 | /// A wrapper type that tells the compiler that the contents might not be valid. |
7 | /// |
8 | /// This is necessary mainly when `T` contains a reference. In that case, the |
9 | /// compiler will sometimes assume that the reference is always valid; in some |
10 | /// cases it will assume this even after the destructor of `T` runs. For |
11 | /// example, when a reference is used as a function argument, then the compiler |
12 | /// will assume that the reference is valid until the function returns, even if |
13 | /// the reference is destroyed during the function. When the reference is used |
14 | /// as part of a self-referential struct, that assumption can be false. Wrapping |
15 | /// the reference in this type prevents the compiler from making that |
16 | /// assumption. |
17 | /// |
18 | /// # Invariants |
19 | /// |
20 | /// The `MaybeUninit` will always contain a valid value until the destructor runs. |
21 | // |
22 | // Reference |
23 | // See <https://users.rust-lang.org/t/unsafe-code-review-semi-owning-weak-rwlock-t-guard/95706> |
24 | // |
25 | // TODO: replace this with an official solution once RFC #3336 or similar is available. |
26 | // <https://github.com/rust-lang/rfcs/pull/3336> |
27 | #[repr (transparent)] |
28 | pub(crate) struct MaybeDangling<T>(MaybeUninit<T>); |
29 | |
30 | impl<T> Drop for MaybeDangling<T> { |
31 | fn drop(&mut self) { |
32 | // Safety: `0` is always initialized. |
33 | unsafe { core::ptr::drop_in_place(self.0.as_mut_ptr()) }; |
34 | } |
35 | } |
36 | |
37 | impl<T> MaybeDangling<T> { |
38 | pub(crate) fn new(inner: T) -> Self { |
39 | Self(MaybeUninit::new(inner)) |
40 | } |
41 | } |
42 | |
43 | impl<F: Future> Future for MaybeDangling<F> { |
44 | type Output = F::Output; |
45 | |
46 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
47 | // Safety: `0` is always initialized. |
48 | let fut = unsafe { self.map_unchecked_mut(|this| this.0.assume_init_mut()) }; |
49 | fut.poll(cx) |
50 | } |
51 | } |
52 | |
53 | #[test] |
54 | fn maybedangling_runs_drop() { |
55 | struct SetOnDrop<'a>(&'a mut bool); |
56 | |
57 | impl Drop for SetOnDrop<'_> { |
58 | fn drop(&mut self) { |
59 | *self.0 = true; |
60 | } |
61 | } |
62 | |
63 | let mut success = false; |
64 | |
65 | drop(MaybeDangling::new(SetOnDrop(&mut success))); |
66 | assert!(success); |
67 | } |
68 | |