facet_core/impls_alloc/
boxed.rs

1use crate::{
2    Def, Facet, KnownSmartPointer, PtrConst, PtrMut, PtrUninit, Shape, SmartPointerDef,
3    SmartPointerFlags, SmartPointerVTable, TryBorrowInnerError, TryFromError, TryIntoInnerError,
4    Type, UserType, ValueVTable, value_vtable,
5};
6
7unsafe impl<'a, T: Facet<'a>> Facet<'a> for alloc::boxed::Box<T> {
8    const VTABLE: &'static ValueVTable = &const {
9        // Define the functions for transparent conversion between Box<T> and T
10        unsafe fn try_from<'a, 'shape, 'src, 'dst, T: Facet<'a>>(
11            src_ptr: PtrConst<'src>,
12            src_shape: &'shape Shape<'shape>,
13            dst: PtrUninit<'dst>,
14        ) -> Result<PtrMut<'dst>, TryFromError<'shape>> {
15            if src_shape.id != T::SHAPE.id {
16                return Err(TryFromError::UnsupportedSourceShape {
17                    src_shape,
18                    expected: &[T::SHAPE],
19                });
20            }
21            let t = unsafe { src_ptr.read::<T>() };
22            let boxed = alloc::boxed::Box::new(t);
23            Ok(unsafe { dst.put(boxed) })
24        }
25
26        unsafe fn try_into_inner<'a, 'src, 'dst, T: Facet<'a>>(
27            src_ptr: PtrMut<'src>,
28            dst: PtrUninit<'dst>,
29        ) -> Result<PtrMut<'dst>, TryIntoInnerError> {
30            let boxed = unsafe { src_ptr.read::<alloc::boxed::Box<T>>() };
31            Ok(unsafe { dst.put(*boxed) })
32        }
33
34        unsafe fn try_borrow_inner<'a, 'src, T: Facet<'a>>(
35            src_ptr: PtrConst<'src>,
36        ) -> Result<PtrConst<'src>, TryBorrowInnerError> {
37            let boxed = unsafe { src_ptr.get::<alloc::boxed::Box<T>>() };
38            Ok(PtrConst::new(&**boxed))
39        }
40
41        let mut vtable = value_vtable!(alloc::boxed::Box<T>, |f, opts| {
42            write!(f, "{}", Self::SHAPE.type_identifier)?;
43            if let Some(opts) = opts.for_children() {
44                write!(f, "<")?;
45                (T::SHAPE.vtable.type_name())(f, opts)?;
46                write!(f, ">")?;
47            } else {
48                write!(f, "<…>")?;
49            }
50            Ok(())
51        });
52        {
53            let vtable = vtable.sized_mut().unwrap();
54            vtable.try_from = || Some(try_from::<T>);
55            vtable.try_into_inner = || Some(try_into_inner::<T>);
56            vtable.try_borrow_inner = || Some(try_borrow_inner::<T>);
57        }
58        vtable
59    };
60
61    const SHAPE: &'static crate::Shape<'static> = &const {
62        // Function to return inner type's shape
63        fn inner_shape<'a, T: Facet<'a>>() -> &'static Shape<'static> {
64            T::SHAPE
65        }
66
67        crate::Shape::builder_for_sized::<Self>()
68            .type_identifier("Box")
69            .type_params(&[crate::TypeParam {
70                name: "T",
71                shape: || T::SHAPE,
72            }])
73            .ty(Type::User(UserType::Opaque))
74            .def(Def::SmartPointer(
75                SmartPointerDef::builder()
76                    .pointee(|| T::SHAPE)
77                    .flags(SmartPointerFlags::EMPTY)
78                    .known(KnownSmartPointer::Box)
79                    .vtable(
80                        &const {
81                            SmartPointerVTable::builder()
82                                .borrow_fn(|this| {
83                                    let ptr = unsafe {
84                                        &raw const **this.as_ptr::<alloc::boxed::Box<T>>()
85                                    };
86                                    PtrConst::new(ptr)
87                                })
88                                .new_into_fn(|this, ptr| {
89                                    let t = unsafe { ptr.read::<T>() };
90                                    let boxed = alloc::boxed::Box::new(t);
91                                    unsafe { this.put(boxed) }
92                                })
93                                .build()
94                        },
95                    )
96                    .build(),
97            ))
98            .inner(inner_shape::<T>)
99            .build()
100    };
101}
102
103#[cfg(test)]
104mod tests {
105    use alloc::boxed::Box;
106    use alloc::string::String;
107
108    use super::*;
109
110    #[test]
111    fn test_box_type_params() {
112        let [type_param_1] = <Box<i32>>::SHAPE.type_params else {
113            panic!("Box<T> should only have 1 type param")
114        };
115        assert_eq!(type_param_1.shape(), i32::SHAPE);
116    }
117
118    #[test]
119    fn test_box_vtable_1_new_borrow_drop() -> eyre::Result<()> {
120        facet_testhelpers::setup();
121
122        let box_shape = <Box<String>>::SHAPE;
123        let box_def = box_shape
124            .def
125            .into_smart_pointer()
126            .expect("Box<T> should have a smart pointer definition");
127
128        // Allocate memory for the Box
129        let box_uninit_ptr = box_shape.allocate()?;
130
131        // Get the function pointer for creating a new Box from a value
132        let new_into_fn = box_def
133            .vtable
134            .new_into_fn
135            .expect("Box<T> should have new_into_fn");
136
137        // Create the value and initialize the Box
138        let mut value = String::from("example");
139        let box_ptr = unsafe { new_into_fn(box_uninit_ptr, PtrMut::new(&raw mut value)) };
140        // The value now belongs to the Box, prevent its drop
141        core::mem::forget(value);
142
143        // Get the function pointer for borrowing the inner value
144        let borrow_fn = box_def
145            .vtable
146            .borrow_fn
147            .expect("Box<T> should have borrow_fn");
148
149        // Borrow the inner value and check it
150        let borrowed_ptr = unsafe { borrow_fn(box_ptr.as_const()) };
151        // SAFETY: borrowed_ptr points to a valid String within the Box
152        assert_eq!(unsafe { borrowed_ptr.get::<String>() }, "example");
153
154        // Get the function pointer for dropping the Box
155        let drop_fn = (box_shape.vtable.sized().unwrap().drop_in_place)()
156            .expect("Box<T> should have drop_in_place");
157
158        // Drop the Box in place
159        // SAFETY: box_ptr points to a valid Box<String>
160        unsafe { drop_fn(box_ptr) };
161
162        // Deallocate the memory
163        // SAFETY: box_ptr was allocated by box_shape and is now dropped (but memory is still valid)
164        unsafe { box_shape.deallocate_mut(box_ptr)? };
165
166        Ok(())
167    }
168}