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}