shapely_core/
slot.rs

1use std::ptr::NonNull;
2
3use crate::{InitMark, ShapeDesc, Shapely, trace};
4
5/// Allows writing into a struct field.
6pub struct Slot<'s> {
7    /// Pointer to where the value will be written. If it's already initialized,
8    /// the old value is dropped in place.
9    ///
10    /// If the shape is a ZST, ptr will be dangling.
11    ptr: NonNull<u8>,
12
13    /// Tracks whether the field is initialized
14    init_mark: InitMark<'s>,
15
16    /// shape of the field we're writing — used for validation
17    shape: ShapeDesc,
18}
19
20impl<'s> Slot<'s> {
21    /// Create a new slot for writing into a struct field — not just `foo.bar`, but also
22    /// `foo.2` for tuples, `foo.0` for newtype wrappers, etc.
23    #[inline(always)]
24    pub fn for_ptr(ptr: NonNull<u8>, shape: ShapeDesc, init_mark: InitMark<'s>) -> Self {
25        Self {
26            ptr,
27            init_mark,
28            shape,
29        }
30    }
31
32    /// Fills the slot with a value of a concrete type. This performs a type check and panics if the
33    /// type is incompatible with the slot's shape.
34    ///
35    /// If the slot is already initialized, the old value is dropped.
36    pub fn fill<T: Shapely>(mut self, value: T) {
37        // should we provide fill_unchecked?
38        if self.shape != T::shape_desc() {
39            panic!(
40                "Attempted to fill a field with an incompatible shape.\n\
41                Expected shape: \x1b[33m{:?}\x1b[0m\n\
42                Actual shape: \x1b[33m{:?}\x1b[0m\n\
43                This is undefined behavior and we're refusing to proceed.",
44                self.shape.get(),
45                T::shape()
46            );
47        }
48
49        if self.init_mark.get() {
50            trace!("Field already initialized, dropping existing value");
51            if let Some(drop_fn) = self.shape.get().drop_in_place {
52                // Safety: The `drop_fn` is guaranteed to be a valid function pointer
53                // for dropping the value at `ptr`. We've already checked that the
54                // shape matches, and we're only calling this if the field is initialized.
55                // The `ptr` is valid because it points to initialized memory.
56                unsafe {
57                    drop_fn(self.ptr.as_ptr());
58                }
59            }
60        }
61
62        trace!(
63            "Filling struct field at address: \x1b[33m{:?}\x1b[0m with type: \x1b[33m{}\x1b[0m",
64            self.ptr,
65            T::shape()
66        );
67        unsafe { std::ptr::write(self.ptr.as_ptr() as *mut T, value) };
68        self.init_mark.set();
69    }
70
71    /// Fill this slot from the given partial.
72    ///
73    /// This is a code smell: if you have a slot, why are you filling it from a heap-allocated
74    /// partial? That's too many allocations.
75    pub fn fill_from_partial(mut self, partial: crate::Partial<'_>) {
76        if self.shape != partial.shape() {
77            panic!(
78                "Attempted to fill a field with an incompatible shape.\n\
79                Expected shape: {:?}\n\
80                Actual shape: {:?}\n\
81                This is undefined behavior and we're refusing to proceed.",
82                self.shape.get(),
83                partial.shape().get()
84            );
85        }
86
87        unsafe {
88            if self.init_mark.get() {
89                if let Some(drop_fn) = self.shape.get().drop_in_place {
90                    drop_fn(self.ptr.as_ptr());
91                }
92            }
93            partial.move_into(self.ptr);
94            self.init_mark.set();
95        }
96    }
97
98    /// Shape getter
99    pub fn shape(&self) -> ShapeDesc {
100        self.shape
101    }
102}