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