lazy_pinned/lib.rs
1#![no_std]
2#![forbid(clippy::undocumented_unsafe_blocks)]
3
4//! See [`LazyPinned`].
5
6use core::pin::Pin;
7
8/// Pinned data which can be lazily initialized.
9///
10/// ## [`LazyPinned<T>`] vs. [`Option<T>`]
11///
12/// [`LazyPinned<T>`] act like [`Option<T>`].
13/// In fact, `LazyPinned<T>` is implemented by just wrapping `Option<T>`.
14/// However, they have different behaviors in pinning projection.
15///
16/// `Pin<P<Option<T>>>` guarantees the `Option<T>` is not moved,
17/// where `P<_>` is a pointer type which deref to `_`.
18/// Thus, when the data is `None`, it cannot be set to `Some(T)` unless
19/// `T: Unpin`.
20///
21/// `Pin<P<LazyPinned<T>>>` only guarantees the inner `T` is pinned.
22/// Thus, `Pin<&mut LazyPinned<T>>` optionally projects to `Pin<&mut T>`
23/// instead of `Pin<P<Option<T>>>`. When `Pin<P<LazyPinned<T>>>` is `None`,
24/// it can be initialized with a value of `T`.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub struct LazyPinned<T>(pub Option<T>);
27
28impl<T> Default for LazyPinned<T> {
29 #[inline]
30 fn default() -> Self {
31 Self(None)
32 }
33}
34
35impl<T> LazyPinned<T> {
36 #[inline]
37 #[must_use]
38 pub fn as_pin_ref(self: Pin<&Self>) -> Option<Pin<&T>> {
39 Pin::get_ref(self).0.as_ref().map(|x| {
40 // SAFETY: `x` is guaranteed to be pinned because it comes from `self`
41 // which is pinned.
42 unsafe { Pin::new_unchecked(x) }
43 })
44 }
45
46 #[inline]
47 #[must_use]
48 pub fn as_pin_mut(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
49 // SAFETY: `get_unchecked_mut` is never used to move the `Option` inside `self`.
50 // `x` is guaranteed to be pinned because it comes from `self` which is pinned.
51 unsafe {
52 Pin::get_unchecked_mut(self)
53 .0
54 .as_mut()
55 .map(|x| Pin::new_unchecked(x))
56 }
57 }
58
59 pub fn pin_project_or_insert(self: Pin<&mut Self>, v: T) -> Pin<&mut T> {
60 // SAFETY: `get_unchecked_mut` is never used to move the `Some(T)` inside `self`.
61 let this = unsafe { Pin::get_unchecked_mut(self) };
62 let x = this.0.get_or_insert(v);
63 // SAFETY: `x` is guaranteed to be pinned because it comes from `self` which is pinned.
64 unsafe { Pin::new_unchecked(x) }
65 }
66
67 pub fn pin_project_or_insert_with(self: Pin<&mut Self>, f: impl FnOnce() -> T) -> Pin<&mut T> {
68 // SAFETY: `get_unchecked_mut` is never used to move the `Some(T)` inside `self`.
69 let this = unsafe { Pin::get_unchecked_mut(self) };
70 let x = this.0.get_or_insert_with(f);
71 // SAFETY: `x` is guaranteed to be pinned because it comes from `self` which is pinned.
72 unsafe { Pin::new_unchecked(x) }
73 }
74
75 pub fn use_pin_or_insert(
76 self: Pin<&mut Self>,
77 use_pin: impl FnOnce(Pin<&mut T>),
78 v: T,
79 ) -> Pin<&mut T> {
80 self.use_pin_or_insert_with(use_pin, move || v)
81 }
82
83 pub fn use_pin_or_insert_with(
84 self: Pin<&mut Self>,
85 use_pin: impl FnOnce(Pin<&mut T>),
86 insert: impl FnOnce() -> T,
87 ) -> Pin<&mut T> {
88 // SAFETY: `get_unchecked_mut` is never used to move the `Some(T)` inside `self`.
89 let this = unsafe { Pin::get_unchecked_mut(self) };
90
91 match &mut this.0 {
92 Some(x) => {
93 // SAFETY: `x` is guaranteed to be pinned because it comes from `self` which is pinned.
94 let mut x = unsafe { Pin::new_unchecked(x) };
95 use_pin(x.as_mut());
96 x
97 }
98 this @ None => {
99 let x = this.insert(insert());
100
101 // SAFETY: `x` is guaranteed to be pinned because it comes from `self` which is pinned.
102 unsafe { Pin::new_unchecked(x) }
103 }
104 }
105 }
106
107 pub fn use_pin_or_insert_with_data<Data>(
108 self: Pin<&mut Self>,
109 data: Data,
110 use_pin: impl FnOnce(Data, Pin<&mut T>),
111 insert: impl FnOnce(Data) -> T,
112 ) -> Pin<&mut T> {
113 // SAFETY: `get_unchecked_mut` is never used to move the `Some(T)` inside `self`.
114 let this = unsafe { Pin::get_unchecked_mut(self) };
115
116 match &mut this.0 {
117 Some(x) => {
118 // SAFETY: `x` is guaranteed to be pinned because it comes from `self` which is pinned.
119 let mut x = unsafe { Pin::new_unchecked(x) };
120 use_pin(data, x.as_mut());
121 x
122 }
123 this @ None => {
124 let x = this.insert(insert(data));
125
126 // SAFETY: `x` is guaranteed to be pinned because it comes from `self` which is pinned.
127 unsafe { Pin::new_unchecked(x) }
128 }
129 }
130 }
131}