1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: MIT OR Apache-2.0 |
3 | |
4 | /*! |
5 | This crate allows you to create ffi-friendly virtual tables. |
6 | |
7 | ## Features |
8 | |
9 | - A [`#[vtable]`](macro@vtable) macro to annotate a VTable struct to generate the traits and structure |
10 | to safely work with it. |
11 | - [`VRef`]/[`VRefMut`]/[`VBox`] types. They are fat reference/box types which wrap a pointer to |
12 | the vtable, and a pointer to the object. |
13 | - [`VRc`]/[`VWeak`] types: equivalent to `std::rc::{Rc, Weak}` types but works with a vtable pointer. |
14 | - Ability to store constants in a vtable. |
15 | - These constants can even be a field offset. |
16 | |
17 | ## Example of use: |
18 | |
19 | ``` |
20 | use vtable::*; |
21 | // we are going to declare a VTable structure for an Animal trait |
22 | #[vtable] |
23 | #[repr(C)] |
24 | struct AnimalVTable { |
25 | /// pointer to a function that makes a noise. The `VRef<AnimalVTable>` is the type of |
26 | /// the self object. |
27 | /// |
28 | /// Note: the #[vtable] macro will automatically add `extern "C"` if that is missing. |
29 | make_noise: fn(VRef<AnimalVTable>, i32) -> i32, |
30 | |
31 | /// if there is a 'drop' member, it is considered as the destructor. |
32 | drop: fn(VRefMut<AnimalVTable>), |
33 | } |
34 | |
35 | struct Dog(i32); |
36 | |
37 | // The #[vtable] macro created the Animal Trait |
38 | impl Animal for Dog { |
39 | fn make_noise(&self, intensity: i32) -> i32 { |
40 | println!("Wof!" ); |
41 | return self.0 * intensity; |
42 | } |
43 | } |
44 | |
45 | // the vtable macro also exposed a macro to create a vtable |
46 | AnimalVTable_static!(static DOG_VT for Dog); |
47 | |
48 | // with that, it is possible to instantiate a VBox |
49 | let animal_box = VBox::<AnimalVTable>::new(Dog(42)); |
50 | assert_eq!(animal_box.make_noise(2), 42 * 2); |
51 | ``` |
52 | |
53 | The [`#[vtable]`](macro@vtable) macro created the "Animal" trait. |
54 | |
55 | Note that the [`#[vtable]`](macro@vtable) macro is applied to the VTable struct so |
56 | that `cbindgen` can see the actual vtable. |
57 | |
58 | */ |
59 | |
60 | #![warn (missing_docs)] |
61 | #![no_std ] |
62 | extern crate alloc; |
63 | |
64 | #[doc (no_inline)] |
65 | pub use const_field_offset::*; |
66 | use core::marker::PhantomData; |
67 | use core::ops::{Deref, DerefMut, Drop}; |
68 | use core::{pin::Pin, ptr::NonNull}; |
69 | #[doc (inline)] |
70 | pub use vtable_macro::*; |
71 | |
72 | mod vrc; |
73 | pub use vrc::*; |
74 | |
75 | /// Internal trait that is implemented by the [`#[vtable]`](macro@vtable) macro. |
76 | /// |
77 | /// # Safety |
78 | /// |
79 | /// The Target object needs to be implemented correctly. |
80 | /// And there should be a `VTable::VTable::new<T>` function that returns a |
81 | /// VTable suitable for the type T. |
82 | pub unsafe trait VTableMeta { |
83 | /// That's the trait object that implements the functions |
84 | /// |
85 | /// NOTE: the size must be `2*size_of::<usize>` |
86 | /// and a `repr(C)` with `(vtable, ptr)` so it has the same layout as |
87 | /// the inner and VBox/VRef/VRefMut |
88 | type Target; |
89 | |
90 | /// That's the VTable itself (so most likely Self) |
91 | type VTable: 'static; |
92 | } |
93 | |
94 | /// This trait is implemented by the [`#[vtable]`](macro@vtable) macro. |
95 | /// |
96 | /// It is implemented if the macro has a "drop" function. |
97 | /// |
98 | /// # Safety |
99 | /// Only the [`#[vtable]`](macro@vtable) macro should implement this trait. |
100 | pub unsafe trait VTableMetaDrop: VTableMeta { |
101 | /// # Safety |
102 | /// `ptr` needs to be pointing to a valid allocated pointer |
103 | unsafe fn drop(ptr: *mut Self::Target); |
104 | /// allocate a new [`VBox`] |
105 | fn new_box<X: HasStaticVTable<Self>>(value: X) -> VBox<Self>; |
106 | } |
107 | |
108 | /// Allow to associate a VTable with a type. |
109 | /// |
110 | /// # Safety |
111 | /// |
112 | /// The VTABLE and STATIC_VTABLE need to be a valid virtual table |
113 | /// corresponding to pointer to Self instance. |
114 | pub unsafe trait HasStaticVTable<VT> |
115 | where |
116 | VT: ?Sized + VTableMeta, |
117 | { |
118 | /// Safety: must be a valid VTable for Self |
119 | fn static_vtable() -> &'static VT::VTable; |
120 | } |
121 | |
122 | #[derive (Copy, Clone)] |
123 | /// The inner structure of VRef, VRefMut, and VBox. |
124 | /// |
125 | /// Invariant: _vtable and _ptr are valid pointer for the lifetime of the container. |
126 | /// _ptr is an instance of the object represented by _vtable |
127 | #[allow (dead_code)] |
128 | #[repr (C)] |
129 | struct Inner { |
130 | vtable: NonNull<u8>, |
131 | ptr: NonNull<u8>, |
132 | } |
133 | |
134 | impl Inner { |
135 | /// Transmute a reference to self into a reference to T::Target. |
136 | fn deref<T: ?Sized + VTableMeta>(&self) -> *const T::Target { |
137 | debug_assert_eq!(core::mem::size_of::<T::Target>(), core::mem::size_of::<Inner>()); |
138 | self as *const Inner as *const T::Target |
139 | } |
140 | |
141 | /// Same as [`Self::deref`]. |
142 | fn deref_mut<T: ?Sized + VTableMeta>(&mut self) -> *mut T::Target { |
143 | debug_assert_eq!(core::mem::size_of::<T::Target>(), core::mem::size_of::<Inner>()); |
144 | self as *mut Inner as *mut T::Target |
145 | } |
146 | } |
147 | |
148 | /// An equivalent of a Box that holds a pointer to a VTable and a pointer to an instance. |
149 | /// A VBox frees the instance when dropped. |
150 | /// |
151 | /// The type parameter is supposed to be the VTable type. |
152 | /// |
153 | /// The VBox implements Deref so one can access all the members of the vtable. |
154 | /// |
155 | /// This is only valid if the VTable has a `drop` function (so that the [`#[vtable]`](macro@vtable) macro |
156 | /// implements the `VTableMetaDrop` trait for it) |
157 | #[repr (transparent)] |
158 | pub struct VBox<T: ?Sized + VTableMetaDrop> { |
159 | inner: Inner, |
160 | phantom: PhantomData<T::Target>, |
161 | } |
162 | |
163 | impl<T: ?Sized + VTableMetaDrop> Deref for VBox<T> { |
164 | type Target = T::Target; |
165 | fn deref(&self) -> &Self::Target { |
166 | unsafe { &*self.inner.deref::<T>() } |
167 | } |
168 | } |
169 | impl<T: ?Sized + VTableMetaDrop> DerefMut for VBox<T> { |
170 | fn deref_mut(&mut self) -> &mut Self::Target { |
171 | unsafe { &mut *(self.inner.deref_mut::<T>() as *mut _) } |
172 | } |
173 | } |
174 | |
175 | impl<T: ?Sized + VTableMetaDrop> Drop for VBox<T> { |
176 | fn drop(&mut self) { |
177 | unsafe { |
178 | T::drop(self.inner.deref::<T>() as *mut _); |
179 | } |
180 | } |
181 | } |
182 | |
183 | impl<T: ?Sized + VTableMetaDrop> VBox<T> { |
184 | /// Create a new VBox from an instance of a type that can be associated with a VTable. |
185 | /// |
186 | /// Will move the instance on the heap. |
187 | /// |
188 | /// (the `HasStaticVTable` is implemented by the `“MyTrait”VTable_static!` macro generated by |
189 | /// the #[vtable] macro) |
190 | pub fn new<X: HasStaticVTable<T>>(value: X) -> Self { |
191 | T::new_box(value) |
192 | } |
193 | |
194 | /// Create a new VBox from raw pointers |
195 | /// # Safety |
196 | /// The `ptr` needs to be a valid object fitting the `vtable`. |
197 | /// ptr must be properly allocated so it can be dropped. |
198 | pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self { |
199 | Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData } |
200 | } |
201 | |
202 | /// Gets a VRef pointing to this box |
203 | pub fn borrow(&self) -> VRef<'_, T> { |
204 | unsafe { VRef::from_inner(self.inner) } |
205 | } |
206 | |
207 | /// Gets a VRefMut pointing to this box |
208 | pub fn borrow_mut(&mut self) -> VRefMut<'_, T> { |
209 | unsafe { VRefMut::from_inner(self.inner) } |
210 | } |
211 | |
212 | /// Leaks the content of the box. |
213 | pub fn leak(self) -> VRefMut<'static, T> { |
214 | let inner = self.inner; |
215 | core::mem::forget(self); |
216 | unsafe { VRefMut::from_inner(inner) } |
217 | } |
218 | } |
219 | |
220 | /// `VRef<'a MyTraitVTable>` can be thought as a `&'a dyn MyTrait` |
221 | /// |
222 | /// It will dereference to a structure that has the same members as MyTrait. |
223 | #[repr (transparent)] |
224 | pub struct VRef<'a, T: ?Sized + VTableMeta> { |
225 | inner: Inner, |
226 | phantom: PhantomData<&'a T::Target>, |
227 | } |
228 | |
229 | // Need to implement manually otherwise it is not implemented if T does not implement Copy / Clone |
230 | impl<'a, T: ?Sized + VTableMeta> Copy for VRef<'a, T> {} |
231 | |
232 | impl<'a, T: ?Sized + VTableMeta> Clone for VRef<'a, T> { |
233 | fn clone(&self) -> Self { |
234 | *self |
235 | } |
236 | } |
237 | |
238 | impl<'a, T: ?Sized + VTableMeta> Deref for VRef<'a, T> { |
239 | type Target = T::Target; |
240 | fn deref(&self) -> &Self::Target { |
241 | unsafe { &*self.inner.deref::<T>() } |
242 | } |
243 | } |
244 | |
245 | impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> { |
246 | /// Create a new VRef from an reference of a type that can be associated with a VTable. |
247 | /// |
248 | /// (the `HasStaticVTable` is implemented by the `“MyTrait”VTable_static!` macro generated by |
249 | /// the #[vtable] macro) |
250 | pub fn new<X: HasStaticVTable<T>>(value: &'a X) -> Self { |
251 | Self { |
252 | inner: Inner { |
253 | vtable: NonNull::from(X::static_vtable()).cast(), |
254 | ptr: NonNull::from(value).cast(), |
255 | }, |
256 | phantom: PhantomData, |
257 | } |
258 | } |
259 | |
260 | /// Create a new Pin<VRef<_>> from a pinned reference. This is similar to `VRef::new`. |
261 | pub fn new_pin<X: HasStaticVTable<T>>(value: core::pin::Pin<&'a X>) -> Pin<Self> { |
262 | // Since Value is pinned, this means it is safe to construct a Pin |
263 | unsafe { |
264 | Pin::new_unchecked(Self { |
265 | inner: Inner { |
266 | vtable: NonNull::from(X::static_vtable()).cast(), |
267 | ptr: NonNull::from(value.get_ref()).cast(), |
268 | }, |
269 | phantom: PhantomData, |
270 | }) |
271 | } |
272 | } |
273 | |
274 | unsafe fn from_inner(inner: Inner) -> Self { |
275 | Self { inner, phantom: PhantomData } |
276 | } |
277 | |
278 | /// Create a new VRef from raw pointers |
279 | /// # Safety |
280 | /// The `ptr` needs to be a valid object fitting the `vtable`. |
281 | /// Both vtable and ptr lifetime must outlive 'a |
282 | pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self { |
283 | Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData } |
284 | } |
285 | |
286 | /// Return a reference of the given type if the type is matching. |
287 | pub fn downcast<X: HasStaticVTable<T>>(&self) -> Option<&X> { |
288 | if self.inner.vtable == NonNull::from(X::static_vtable()).cast() { |
289 | // Safety: We just checked that the vtable fits |
290 | unsafe { Some(self.inner.ptr.cast().as_ref()) } |
291 | } else { |
292 | None |
293 | } |
294 | } |
295 | |
296 | /// Return a reference of the given type if the type is matching |
297 | pub fn downcast_pin<X: HasStaticVTable<T>>(this: Pin<Self>) -> Option<Pin<&'a X>> { |
298 | let inner = unsafe { Pin::into_inner_unchecked(this).inner }; |
299 | if inner.vtable == NonNull::from(X::static_vtable()).cast() { |
300 | // Safety: We just checked that the vtable fits |
301 | unsafe { Some(Pin::new_unchecked(inner.ptr.cast().as_ref())) } |
302 | } else { |
303 | None |
304 | } |
305 | } |
306 | |
307 | /// Returns a pointer to the VRef's instance. This is primarily useful for comparisons. |
308 | pub fn as_ptr(this: Self) -> NonNull<u8> { |
309 | this.inner.ptr |
310 | } |
311 | } |
312 | |
313 | /// `VRefMut<'a MyTraitVTable>` can be thought as a `&'a mut dyn MyTrait` |
314 | /// |
315 | /// It will dereference to a structure that has the same members as MyTrait. |
316 | #[repr (transparent)] |
317 | pub struct VRefMut<'a, T: ?Sized + VTableMeta> { |
318 | inner: Inner, |
319 | phantom: PhantomData<&'a mut T::Target>, |
320 | } |
321 | |
322 | impl<'a, T: ?Sized + VTableMeta> Deref for VRefMut<'a, T> { |
323 | type Target = T::Target; |
324 | fn deref(&self) -> &Self::Target { |
325 | unsafe { &*self.inner.deref::<T>() } |
326 | } |
327 | } |
328 | |
329 | impl<'a, T: ?Sized + VTableMeta> DerefMut for VRefMut<'a, T> { |
330 | fn deref_mut(&mut self) -> &mut Self::Target { |
331 | unsafe { &mut *(self.inner.deref_mut::<T>() as *mut _) } |
332 | } |
333 | } |
334 | |
335 | impl<'a, T: ?Sized + VTableMeta> VRefMut<'a, T> { |
336 | /// Create a new VRef from a mutable reference of a type that can be associated with a VTable. |
337 | /// |
338 | /// (the `HasStaticVTable` is implemented by the `“MyTrait”VTable_static!` macro generated by |
339 | /// the #[vtable] macro) |
340 | pub fn new<X: HasStaticVTable<T>>(value: &'a mut X) -> Self { |
341 | Self { |
342 | inner: Inner { |
343 | vtable: NonNull::from(X::static_vtable()).cast(), |
344 | ptr: NonNull::from(value).cast(), |
345 | }, |
346 | phantom: PhantomData, |
347 | } |
348 | } |
349 | |
350 | unsafe fn from_inner(inner: Inner) -> Self { |
351 | Self { inner, phantom: PhantomData } |
352 | } |
353 | |
354 | /// Create a new VRefMut from raw pointers |
355 | /// # Safety |
356 | /// The `ptr` needs to be a valid object fitting the `vtable`. |
357 | /// Both vtable and ptr lifetime must outlive 'a. |
358 | /// Can create mutable reference to ptr, so no other code can create mutable reference of ptr |
359 | /// during the life time 'a. |
360 | pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self { |
361 | Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData } |
362 | } |
363 | |
364 | /// Borrow this to obtain a VRef. |
365 | pub fn borrow(&self) -> VRef<'_, T> { |
366 | unsafe { VRef::from_inner(self.inner) } |
367 | } |
368 | |
369 | /// Borrow this to obtain a new VRefMut. |
370 | pub fn borrow_mut(&mut self) -> VRefMut<'_, T> { |
371 | unsafe { VRefMut::from_inner(self.inner) } |
372 | } |
373 | |
374 | /// Create a VRef with the same lifetime as the original lifetime. |
375 | pub fn into_ref(self) -> VRef<'a, T> { |
376 | unsafe { VRef::from_inner(self.inner) } |
377 | } |
378 | |
379 | /// Return a reference of the given type if the type is matching. |
380 | pub fn downcast<X: HasStaticVTable<T>>(&mut self) -> Option<&mut X> { |
381 | if self.inner.vtable == NonNull::from(X::static_vtable()).cast() { |
382 | // Safety: We just checked that the vtable fits |
383 | unsafe { Some(self.inner.ptr.cast().as_mut()) } |
384 | } else { |
385 | None |
386 | } |
387 | } |
388 | } |
389 | |
390 | /** Creates a [`VRef`] or a [`VRefMut`] suitable for an instance that implements the trait |
391 | |
392 | When possible, [`VRef::new`] or [`VRefMut::new`] should be preferred, as they use a static vtable. |
393 | But when using the generated `XxxVTable_static!` macro that is not possible and this macro can be |
394 | used instead. |
395 | Note that the `downcast` will not work with references created with this macro. |
396 | |
397 | ``` |
398 | use vtable::*; |
399 | #[vtable] |
400 | struct MyVTable { /* ... */ } |
401 | struct Something { /* ... */}; |
402 | impl My for Something {}; |
403 | |
404 | let mut s = Something { /* ... */}; |
405 | // declare a my_vref variable for the said VTable |
406 | new_vref!(let my_vref : VRef<MyVTable> for My = &s); |
407 | |
408 | // same but mutable |
409 | new_vref!(let mut my_vref_m : VRefMut<MyVTable> for My = &mut s); |
410 | |
411 | ``` |
412 | */ |
413 | #[macro_export ] |
414 | macro_rules! new_vref { |
415 | (let $ident:ident : VRef<$vtable:ty> for $trait_:path = $e:expr) => { |
416 | // ensure that the type of the expression is correct |
417 | let vtable = { |
418 | use $crate::VTableMeta; |
419 | fn get_vt<X: $trait_>(_: &X) -> <$vtable as VTableMeta>::VTable { |
420 | <$vtable as VTableMeta>::VTable::new::<X>() |
421 | } |
422 | get_vt($e) |
423 | }; |
424 | |
425 | let $ident = { |
426 | use $crate::VTableMeta; |
427 | fn create<'a, X: $trait_>( |
428 | vtable: &'a <$vtable as VTableMeta>::VTable, |
429 | val: &'a X, |
430 | ) -> $crate::VRef<'a, <$vtable as VTableMeta>::VTable> { |
431 | use ::core::ptr::NonNull; |
432 | // Safety: we constructed the vtable such that it fits for the value |
433 | unsafe { $crate::VRef::from_raw(NonNull::from(vtable), NonNull::from(val).cast()) } |
434 | } |
435 | create(&vtable, $e) |
436 | }; |
437 | }; |
438 | (let mut $ident:ident : VRefMut<$vtable:ty> for $trait_:path = $e:expr) => { |
439 | // ensure that the type of the expression is correct |
440 | let vtable = { |
441 | use $crate::VTableMeta; |
442 | fn get_vt<X: $trait_>(_: &mut X) -> <$vtable as VTableMeta>::VTable { |
443 | <$vtable as VTableMeta>::VTable::new::<X>() |
444 | } |
445 | get_vt($e) |
446 | }; |
447 | |
448 | let mut $ident = { |
449 | use $crate::VTableMeta; |
450 | fn create<'a, X: $trait_>( |
451 | vtable: &'a <$vtable as VTableMeta>::VTable, |
452 | val: &'a mut X, |
453 | ) -> $crate::VRefMut<'a, <$vtable as VTableMeta>::VTable> { |
454 | use ::core::ptr::NonNull; |
455 | // Safety: we constructed the vtable such that it fits for the value |
456 | unsafe { |
457 | $crate::VRefMut::from_raw(NonNull::from(vtable), NonNull::from(val).cast()) |
458 | } |
459 | } |
460 | create(&vtable, $e) |
461 | }; |
462 | }; |
463 | } |
464 | |
465 | /// Represents an offset to a field of type matching the vtable, within the Base container structure. |
466 | #[repr (C)] |
467 | pub struct VOffset<Base, T: ?Sized + VTableMeta, PinFlag = NotPinned> { |
468 | vtable: &'static T::VTable, |
469 | /// Safety invariant: the vtable is valid, and the field at the given offset within Base is |
470 | /// matching with the vtable |
471 | offset: usize, |
472 | phantom: PhantomData<FieldOffset<Base, (), PinFlag>>, |
473 | } |
474 | |
475 | impl<Base, T: ?Sized + VTableMeta, PinFlag> core::fmt::Debug for VOffset<Base, T, PinFlag> { |
476 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
477 | write!(f, "VOffset( {})" , self.offset) |
478 | } |
479 | } |
480 | |
481 | impl<Base, T: ?Sized + VTableMeta, Flag> VOffset<Base, T, Flag> { |
482 | /// Apply this offset to a reference to the base to obtain a [`VRef`] with the same |
483 | /// lifetime as the base lifetime |
484 | #[inline ] |
485 | pub fn apply(self, base: &Base) -> VRef<'_, T> { |
486 | let ptr = base as *const Base as *const u8; |
487 | unsafe { |
488 | VRef::from_raw( |
489 | NonNull::from(self.vtable), |
490 | NonNull::new_unchecked(ptr.add(self.offset) as *mut _), |
491 | ) |
492 | } |
493 | } |
494 | |
495 | /// Apply this offset to a reference to the base to obtain a [`VRefMut`] with the same |
496 | /// lifetime as the base lifetime |
497 | #[inline ] |
498 | pub fn apply_mut(self, base: &mut Base) -> VRefMut<'_, T> { |
499 | let ptr = base as *mut Base as *mut u8; |
500 | unsafe { |
501 | VRefMut::from_raw( |
502 | NonNull::from(self.vtable), |
503 | NonNull::new_unchecked(ptr.add(self.offset)), |
504 | ) |
505 | } |
506 | } |
507 | |
508 | /// Create an new VOffset from a [`FieldOffset`] where the target type implement the |
509 | /// [`HasStaticVTable`] trait. |
510 | #[inline ] |
511 | pub fn new<X: HasStaticVTable<T>>(o: FieldOffset<Base, X, Flag>) -> Self { |
512 | Self { vtable: X::static_vtable(), offset: o.get_byte_offset(), phantom: PhantomData } |
513 | } |
514 | |
515 | /// Create a new VOffset from raw data |
516 | /// |
517 | /// # Safety |
518 | /// There must be a field that matches the vtable at offset T in base. |
519 | #[inline ] |
520 | pub unsafe fn from_raw(vtable: &'static T::VTable, offset: usize) -> Self { |
521 | Self { vtable, offset, phantom: PhantomData } |
522 | } |
523 | } |
524 | |
525 | impl<Base, T: ?Sized + VTableMeta> VOffset<Base, T, AllowPin> { |
526 | /// Apply this offset to a reference to the base to obtain a `Pin<VRef<'a, T>>` with the same |
527 | /// lifetime as the base lifetime |
528 | #[inline ] |
529 | pub fn apply_pin(self, base: Pin<&Base>) -> Pin<VRef<T>> { |
530 | let ptr: *mut u8 = base.get_ref() as *const Base as *mut u8; |
531 | unsafe { |
532 | Pin::new_unchecked(pointer:VRef::from_raw( |
533 | vtable:NonNull::from(self.vtable), |
534 | ptr:NonNull::new_unchecked(ptr:ptr.add(self.offset)), |
535 | )) |
536 | } |
537 | } |
538 | } |
539 | |
540 | // Need to implement manually otherwise it is not implemented if T does not implement Copy / Clone |
541 | impl<Base, T: ?Sized + VTableMeta, Flag> Copy for VOffset<Base, T, Flag> {} |
542 | |
543 | impl<Base, T: ?Sized + VTableMeta, Flag> Clone for VOffset<Base, T, Flag> { |
544 | fn clone(&self) -> Self { |
545 | *self |
546 | } |
547 | } |
548 | |
549 | #[cfg (doctest)] |
550 | mod compile_fail_tests; |
551 | |
552 | /// re-export for the macro |
553 | #[doc (hidden)] |
554 | pub mod internal { |
555 | pub use alloc::alloc::dealloc; |
556 | pub use alloc::boxed::Box; |
557 | } |
558 | |