emplace_init/lib.rs
1#![feature(ptr_as_uninit)]
2use core::pin::Pin;
3use core::mem::MaybeUninit;
4
5/// A type that can be constructed in-place.
6///
7/// This trait is unsafe to implement due to the constraints on `emplace`.
8pub unsafe trait Emplace<Args>: Sized {
9 /// Initializes `place` with a valid value of `Self` using the given arguments.
10 ///
11 /// ## Safety (implementer)
12 ///
13 /// The implementor *must* initialize `place`. Callers are allowed to assume that `place` is
14 /// initialized after this returns.
15 ///
16 /// ## Safety (caller)
17 ///
18 /// This function is safe to call only when `place` is uninitialized.
19 /// Otherwise, it could be used to move out of a pinned value.
20 //
21 // FIXME: Perhaps this should return a "witness of the initializedness of `place`" (e.g.
22 // `Pin<&mut Self`)? If callers were forced to check that the address of the returned pointer
23 // was equal to that of the input pointer, I think the trait itself would be safe to implement.
24 unsafe fn emplace(place: Pin<&mut MaybeUninit<Self>>, args: Args);
25}
26
27/// Initializes a struct, field by field, in-place on the heap.
28///
29/// ## Examples
30///
31/// A translated version of the code from the [Rust-for-Linux talk at CTCFT][CTCFT] (Nov 2021).
32///
33/// [CTCFT]: https://youtu.be/azcrUzeY3Pw?t=680
34///
35/// ```ignore
36/// struct SharedState {
37/// state_changed: CondVar,
38/// inner: Mutex<usize>,
39/// }
40///
41/// fn try_new() -> Result<Ref<Self>> {
42/// let ret = emplace!(UniqueRef <- SharedState {
43/// state_changed <- CondVar("SharedState::state_changed"),
44/// inner <- Mutex(0, "SharedState::inner"),
45/// })?;
46///
47/// Ok(ret.into())
48/// }
49/// ```
50///
51/// This assumes [`Emplace`] is implemented for `Mutex` and `CondVar` and would require a small
52/// tweak to the macro to support fallible allocation (`try_emplace`?).
53#[macro_export]
54macro_rules! emplace {
55 ($P:ident <- $S:ident { $($fields:tt)* }) => {{
56 use core::pin::Pin;
57 use core::mem::{self, MaybeUninit};
58 #[allow(unused)]
59 use core::ptr::{self, addr_of_mut};
60
61 let mut ret = $P::pin(MaybeUninit::uninit());
62 let mut pret: Pin<&mut MaybeUninit<$S>> = ret.as_mut();
63
64 emplace!(@INIT [pret] $($fields)*);
65
66 // SAFETY: At this point, all fields that were given to the macro have been initialized.
67 // The exhaustive pattern match below asserts that these are *all* fields of `S`, so `S` is
68 // fully initialized. Therefore, it is safe to cast away the `MaybeUninit` (which is
69 // `#[repr(transparent)]`).
70 let ret: Pin<$P<$S>> = unsafe { mem::transmute(ret) };
71 let emplace!{ @PAT $S { $($fields)* }} = ret.as_ref().get_ref();
72
73 ret
74 }};
75
76 // @INIT -- (Recursive) Initializes each field.
77
78 (@INIT [$pret:ident]) => {};
79
80 // Project a pinned mut ref to uninitialized `S` to a pinned mut ref of one of `S's
81 // fields. Initialize it with `emplace`
82 (@INIT [$pret:ident] $field:ident <- $F:ident ( $($args:expr),* $(,)? ), $($rest:tt)*) => {
83 // SAFETY: $pret is uninitialized.
84 unsafe {
85 let pfield: Pin<&mut MaybeUninit<_>> = $pret
86 .as_mut()
87 .map_unchecked_mut(|p| {
88 let pfield = addr_of_mut!((*p.as_mut_ptr()).$field).as_uninit_mut();
89 pfield.unwrap() // cannot fail since `p` was non-null.
90 });
91
92 $F::emplace(pfield, ($($args),*));
93 }
94
95 emplace!(@INIT [$pret] $($rest)*);
96 };
97
98 (@INIT [$pret:ident] $field:ident : $expr:expr, $($rest:tt)*) => {
99 unsafe {
100 let pfield = addr_of_mut!((*$pret.as_mut().get_unchecked_mut().as_mut_ptr()).$field);
101 ptr::write(pfield, $expr);
102 }
103
104 emplace!(@INIT [$pret] $($rest)*);
105 };
106
107 // @PAT -- Generates an exhaustive match for the struct we're constructing.
108 // This ensures all fields are initialized.
109
110 (@PAT $S:ident { $( $field:ident $(:)? $(<-)? $_:expr, )* }) => {
111 $S { $( $field : _, )* }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use core::cell::Cell;
119 use core::marker::PhantomPinned;
120
121 struct Mutex<T> {
122 data: T,
123 name: Cell<Option<&'static str>>,
124 _pinned: PhantomPinned,
125 }
126
127 impl<T> Mutex<T> {
128 unsafe fn new(x: T) -> Self {
129 Mutex {
130 data: x,
131 name: Cell::new(None),
132 _pinned: PhantomPinned,
133 }
134 }
135
136 fn init(self: Pin<&mut Self>, s: &'static str) {
137 self.into_ref().get_ref().name.set(Some(s))
138 }
139
140 fn name(&self) -> &'static str {
141 self.name.get().unwrap()
142 }
143 }
144
145 unsafe impl<T> Emplace<(T, &'static str)> for Mutex<T> {
146 #[warn(unsafe_op_in_unsafe_fn)]
147 unsafe fn emplace(place: Pin<&mut MaybeUninit<Self>>, args: (T, &'static str)) {
148 // SAFETY:
149 // - `Mutex::new`: `Mutex::init` is called below.
150 // - `map_unchecked_mut`: `emplace` requires that `place` is uninitialized, so nothing is
151 // actually pinned.
152 let this = unsafe {
153 place.map_unchecked_mut(|p| p.write(Mutex::new(args.0)))
154 };
155
156 Mutex::init(this, args.1)
157 }
158 }
159
160 struct SharedState {
161 x: u32,
162 y: u32,
163 inner: Mutex<i32>,
164 other: Mutex<i64>,
165 }
166
167 impl SharedState {
168 fn new() -> Pin<Box<Self>> {
169 emplace!(Box <- Self {
170 other <- Mutex(0, "SharedState::other"),
171 x: 1,
172 inner <- Mutex(0, "SharedState::inner"),
173 y: 3,
174 })
175 }
176 }
177
178 #[test]
179 fn emplace() {
180 let x = SharedState::new();
181 let x = x.as_ref().get_ref();
182 assert_eq!("SharedState::inner", x.inner.name());
183 assert_eq!("SharedState::other", x.other.name());
184 let _ = x.other.data;
185 }
186}
187