Skip to main content

rootcause_internals/attachment/
raw.rs

1//! Type-erased attachment pointer types.
2//!
3//! This module encapsulates the `ptr` field of [`RawAttachment`] and
4//! [`RawAttachmentRef`], ensuring it is only visible within this module. This
5//! visibility restriction guarantees the safety invariant: **the pointer always
6//! comes from `Box<AttachmentData<A>>`**.
7//!
8//! # Safety Invariant
9//!
10//! Since the `ptr` field can only be set via [`RawAttachment::new`] (which
11//! creates it from `Box::into_raw`), and cannot be modified afterward (no `pub`
12//! or `pub(crate)` fields), the pointer provenance remains valid throughout the
13//! value's lifetime.
14//!
15//! The [`RawAttachment::drop`] implementation relies on this invariant to
16//! safely reconstruct the `Box` and deallocate the memory.
17//!
18//! # Type Erasure
19//!
20//! The concrete type parameter `A` is erased by casting to
21//! `AttachmentData<Erased>`. The vtable stored within the `AttachmentData`
22//! provides the runtime type information needed to safely downcast and format
23//! attachments.
24
25use alloc::boxed::Box;
26use core::{
27    any::{Any, TypeId},
28    ptr::NonNull,
29};
30
31use crate::{
32    attachment::data::AttachmentData,
33    handlers::{AttachmentFormattingStyle, AttachmentHandler, FormattingFunction},
34    util::Erased,
35};
36
37/// A pointer to an [`AttachmentData`] that is guaranteed to point to an
38/// initialized instance of an [`AttachmentData<A>`] for some specific `A`,
39/// though we do not know which actual `A` it is.
40///
41/// However, the pointer is allowed to transition into a non-initialized state
42/// inside the [`RawAttachment::drop`] method.
43///
44/// The pointer is guaranteed to have been created using [`Box::into_raw`].
45///
46/// We cannot use a [`Box<AttachmentData<A>>`] directly, because that does not
47/// allow us to type-erase the `A`.
48#[repr(transparent)]
49pub struct RawAttachment {
50    /// Pointer to the inner attachment data
51    ///
52    /// # Safety
53    ///
54    /// The following safety invariants are guaranteed to be upheld as long as
55    /// this struct exists:
56    ///
57    /// 1. The pointer must have been created from a `Box<AttachmentData<A>>`
58    ///    for some `A` using `Box::into_raw`.
59    /// 2. The pointer will point to the same `AttachmentData<A>` for the entire
60    ///    lifetime of this object.
61    /// 3. The pointee is properly initialized for the entire lifetime of this
62    ///    object, except during the execution of the `Drop` implementation.
63    /// 4. The pointer is the sole owner of the `AttachmentData` instance.
64    ptr: NonNull<AttachmentData<Erased>>,
65}
66
67impl RawAttachment {
68    /// Creates a new [`RawAttachment`] with the specified handler and
69    /// attachment.
70    ///
71    /// The returned attachment will embed the specified attachment and use the
72    /// specified handler for all operations.
73    #[inline]
74    pub fn new<A, H>(attachment: A) -> Self
75    where
76        A: 'static,
77        H: AttachmentHandler<A>,
78    {
79        let ptr = Box::new(AttachmentData::new::<H>(attachment));
80        let ptr: *mut AttachmentData<A> = Box::into_raw(ptr);
81        let ptr: *mut AttachmentData<Erased> = ptr.cast::<AttachmentData<Erased>>();
82
83        // SAFETY: `Box::into_raw` returns a non-null pointer
84        let ptr: NonNull<AttachmentData<Erased>> = unsafe {
85            // @add-unsafe-context: Erased
86            NonNull::new_unchecked(ptr)
87        };
88
89        Self {
90            // SAFETY:
91            // 1. See above
92            // 2. N/A
93            // 3. N/A
94            // 4. The Box is consumed, so we are the sole owner
95            ptr,
96        }
97    }
98
99    /// Returns a reference to the [`AttachmentData`] instance.
100    #[inline]
101    pub fn as_ref(&self) -> RawAttachmentRef<'_> {
102        RawAttachmentRef {
103            // SAFETY:
104            // 1. The pointer comes from `Box::into_raw` (guaranteed by `self`'s invariant)
105            // 2. We are creating the `RawAttachmentRef` here, and we are not changing the pointer
106            // 3. The returned `RawAttachmentRef<'b>` represents shared access for lifetime `'b`,
107            //    which is valid because we borrow `self` for `'b`, preventing any mutation through
108            //    `self` while the returned reference exists
109            ptr: self.ptr,
110            _marker: core::marker::PhantomData,
111        }
112    }
113
114    /// Returns a mutable reference to the [`AttachmentData`] instance.
115    #[inline]
116    pub fn as_mut(&mut self) -> RawAttachmentMut<'_> {
117        RawAttachmentMut {
118            // SAFETY:
119            // 1. Upheld by invariant on `self`
120            // 2. We are creating the `RawAttachmentMut` here, and we are not changing the pointer
121            // 3. Upheld by mutable borrow of `self`
122            ptr: self.ptr,
123            _marker: core::marker::PhantomData,
124        }
125    }
126}
127
128impl core::ops::Drop for RawAttachment {
129    #[inline]
130    fn drop(&mut self) {
131        let vtable = self.as_ref().vtable();
132
133        // SAFETY:
134        // 1. The pointer comes from `Box::into_raw` (guaranteed by
135        //    `RawAttachment::new`)
136        // 2. The vtable returned by `self.as_ref().vtable()` is guaranteed to match the
137        //    data in the `AttachmentData`.
138        // 3. The pointer is initialized and has not been previously freed as guaranteed
139        //    by the invariants on this type. We are correctly transferring ownership
140        //    here and the pointer is not used afterwards, as we are in the drop
141        //    function.
142        unsafe {
143            // @add-unsafe-context: AttachmentData
144            vtable.drop(self.ptr);
145        }
146    }
147}
148
149/// A lifetime-bound pointer to an [`AttachmentData`] that is guaranteed to
150/// point to an initialized instance of an [`AttachmentData<A>`] for some
151/// specific `A`, though we do not know which actual `A` it is.
152///
153/// We cannot use a [`&'a AttachmentData<A>`] directly, because that would
154/// require us to know the actual type of the attachment, which we do not.
155///
156/// [`&'a AttachmentData<A>`]: AttachmentData
157#[derive(Clone, Copy)]
158#[repr(transparent)]
159pub struct RawAttachmentRef<'a> {
160    /// Pointer to the inner attachment data
161    ///
162    /// # Safety
163    ///
164    /// The following safety invariants are guaranteed to be upheld as long as
165    /// this struct exists:
166    ///
167    /// 1. The pointer must have been created from a `Box<AttachmentData<A>>`
168    ///    for some `A` using `Box::into_raw`.
169    /// 2. The pointer will point to the same `AttachmentData<A>` for the entire
170    ///    lifetime of this object.
171    /// 3. This pointer represents read-only access to the `AttachmentData` for
172    ///    the lifetime `'a` with the same semantics as a `&'a
173    ///    AttachmentData<C>`.
174    ptr: NonNull<AttachmentData<Erased>>,
175
176    /// Marker to tell the compiler that we should
177    /// behave the same as a `&'a AttachmentData<Erased>`
178    _marker: core::marker::PhantomData<&'a AttachmentData<Erased>>,
179}
180
181impl<'a> RawAttachmentRef<'a> {
182    /// Casts the [`RawAttachmentRef`] to an [`AttachmentData<A>`] reference.
183    ///
184    /// # Safety
185    ///
186    /// The caller must ensure:
187    ///
188    /// 1. The type `A` matches the actual attachment type stored in the
189    ///    [`AttachmentData`].
190    #[inline]
191    pub(super) unsafe fn cast_inner<A>(self) -> &'a AttachmentData<A> {
192        // Debug assertion to catch type mismatches in case of bugs
193        debug_assert_eq!(self.vtable().type_id(), TypeId::of::<A>());
194
195        let this = self.ptr.cast::<AttachmentData<A>>();
196        // SAFETY: Converting the NonNull pointer to a reference is sound because:
197        // - The pointer is non-null, properly aligned, and dereferenceable (guaranteed
198        //   by RawAttachmentRef's type invariants)
199        // - The pointee is properly initialized (RawAttachmentRef's doc comment
200        //   guarantees it points to an initialized AttachmentData<A> for some A)
201        // - The type `A` matches the actual attachment type (guaranteed by caller)
202        // - Shared access is allowed
203        // - The reference lifetime 'a is valid (tied to RawAttachmentRef<'a>'s
204        //   lifetime)
205        unsafe { this.as_ref() }
206    }
207
208    /// Returns a [`NonNull`] pointer to the [`AttachmentData`] instance.
209    #[inline]
210    pub(super) fn as_ptr(self) -> *const AttachmentData<Erased> {
211        self.ptr.as_ptr()
212    }
213
214    /// Returns the [`TypeId`] of the attachment.
215    #[inline]
216    pub fn attachment_type_id(self) -> TypeId {
217        self.vtable().type_id()
218    }
219
220    /// Returns the [`core::any::type_name`] of the attachment.
221    #[inline]
222    pub fn attachment_type_name(self) -> &'static str {
223        self.vtable().type_name()
224    }
225
226    /// Returns the [`TypeId`] of the attachment handler.
227    #[inline]
228    pub fn attachment_handler_type_id(self) -> TypeId {
229        self.vtable().handler_type_id()
230    }
231
232    /// Formats the attachment by using the [`AttachmentHandler::display`]
233    /// method specified by the handler used to create the
234    /// [`AttachmentData`].
235    #[inline]
236    pub fn attachment_display(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
237        let vtable = self.vtable();
238        // SAFETY:
239        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
240        //    the `AttachmentData`.
241        unsafe {
242            // @add-unsafe-context: AttachmentData
243            vtable.display(self, formatter)
244        }
245    }
246
247    /// Formats the attachment by using the [`AttachmentHandler::debug`] method
248    /// specified by the handler used to create the [`AttachmentData`].
249    #[inline]
250    pub fn attachment_debug(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251        let vtable = self.vtable();
252
253        // SAFETY:
254        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
255        //    the `AttachmentData`.
256        unsafe {
257            // @add-unsafe-context: AttachmentData
258            vtable.debug(self, formatter)
259        }
260    }
261
262    /// Returns a [`&dyn Any`](Any) view of the attachment.
263    ///
264    /// The returned reference can be downcast using
265    /// `<dyn Any>::downcast_ref`.
266    #[inline]
267    pub fn attachment_as_any(self) -> &'a (dyn Any + 'static) {
268        let vtable = self.vtable();
269        // SAFETY:
270        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
271        //    the `AttachmentData`.
272        unsafe {
273            // @add-unsafe-context: AttachmentData
274            vtable.attachment_as_any(self)
275        }
276    }
277
278    /// The formatting style preferred by the attachment when formatted as part
279    /// of a report.
280    ///
281    /// # Arguments
282    ///
283    /// - `report_formatting_function`: Whether the report in which this
284    ///   attachment will be embedded is being formatted using [`Display`]
285    ///   formatting or [`Debug`]
286    ///
287    /// [`Display`]: core::fmt::Display
288    /// [`Debug`]: core::fmt::Debug
289    #[inline]
290    pub fn preferred_formatting_style(
291        self,
292        report_formatting_function: FormattingFunction,
293    ) -> AttachmentFormattingStyle {
294        let vtable = self.vtable();
295
296        // SAFETY:
297        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
298        //    the `AttachmentData`.
299        unsafe {
300            // @add-unsafe-context: AttachmentData
301            vtable.preferred_formatting_style(self, report_formatting_function)
302        }
303    }
304}
305
306/// A mutable lifetime-bound pointer to an [`AttachmentData`] that is guaranteed
307/// to be the sole mutable pointer to an initialized instance of an
308/// [`AttachmentData<A>`] for some specific `A`, though we do not know which
309/// actual `A` it is.
310///
311/// We cannot use a [`&'a mut AttachmentData<A>`] directly, because that would
312/// require us to know the actual type of the attachment, which we do not.
313///
314/// [`&'a mut AttachmentData<A>`]: AttachmentData
315#[repr(transparent)]
316pub struct RawAttachmentMut<'a> {
317    /// Pointer to the inner attachment data
318    ///
319    /// # Safety
320    ///
321    /// The following safety invariants are guaranteed to be upheld as long as
322    /// this struct exists:
323    ///
324    /// 1. The pointer must have been created from a `Box<AttachmentData<A>>`
325    ///    for some `A` using `Box::into_raw`.
326    /// 2. The pointer will point to the same `AttachmentData<A>` for the entire
327    ///    lifetime of this object.
328    /// 3. This pointer represents exclusive mutable access to the
329    ///    `AttachmentData` for the lifetime `'a` with the same semantics as a
330    ///    `&'a mut AttachmentData<C>`.
331    ptr: NonNull<AttachmentData<Erased>>,
332
333    /// Marker to tell the compiler that we should
334    /// behave the same as a `&'a mut AttachmentData<Erased>`
335    _marker: core::marker::PhantomData<&'a mut AttachmentData<Erased>>,
336}
337
338impl<'a> RawAttachmentMut<'a> {
339    /// Casts the [`RawAttachmentMut`] to an [`AttachmentData<A>`] mutable
340    /// reference.
341    ///
342    /// # Safety
343    ///
344    /// The caller must ensure:
345    ///
346    /// 1. The type `A` matches the actual attachment type stored in the
347    ///    [`AttachmentData`].
348    #[inline]
349    pub(super) unsafe fn cast_inner<A>(self) -> &'a mut AttachmentData<A> {
350        // Debug assertion to catch type mismatches in case of bugs
351        debug_assert_eq!(self.as_ref().vtable().type_id(), TypeId::of::<A>());
352
353        let mut this = self.ptr.cast::<AttachmentData<A>>();
354        // SAFETY: Converting the NonNull pointer to a mutable reference is sound
355        // because:
356        // - The pointer is non-null, properly aligned, and dereferenceable (guaranteed
357        //   by RawAttachmentMut's type invariants)
358        // - The pointee is properly initialized (RawAttachmentMut's doc comment
359        //   guarantees it is the exclusive pointer to an initialized AttachmentData<A>
360        //   for some A)
361        // - The type `A` matches the actual attachment type (guaranteed by caller)
362        // - Shared access is NOT allowed
363        // - The reference lifetime 'a is valid (tied to RawAttachmentMut<'a>'s
364        //   lifetime)
365        unsafe { this.as_mut() }
366    }
367
368    /// Reborrows the mutable reference to the [`AttachmentData`] with a shorter
369    /// lifetime.
370    #[inline]
371    pub fn reborrow<'b>(&'b mut self) -> RawAttachmentMut<'b> {
372        RawAttachmentMut {
373            // SAFETY:
374            // 1. The pointer comes from `Box::into_raw` (guaranteed by `self`'s invariant)
375            // 2. We are creating the `RawAttachmentMut` here, and we are not changing the pointer
376            // 3. Exclusive mutable access for lifetime `'b` is guaranteed because:
377            //    - The returned `RawAttachmentMut<'b>` contains `PhantomData<&'b mut ...>`
378            //    - This causes the borrow checker to treat the return value as borrowing `self` for
379            //      `'b`
380            //    - Therefore `self` cannot be used while the returned value exists
381            ptr: self.ptr,
382            _marker: core::marker::PhantomData,
383        }
384    }
385
386    /// Returns a reference to the [`AttachmentData`] instance.
387    #[inline]
388    pub fn as_ref<'b: 'a>(&'b self) -> RawAttachmentRef<'b> {
389        RawAttachmentRef {
390            // SAFETY:
391            // 1. The pointer comes from `Box::into_raw` (guaranteed by `self`'s invariant)
392            // 2. We are creating the `RawAttachmentRef` here, and we are not changing the pointer
393            // 3. The returned `RawAttachmentRef<'b>` represents shared access for lifetime `'b`,
394            //    which is valid because we borrow `self` for `'b`, preventing any mutation through
395            //    `self` while the returned reference exists
396            ptr: self.ptr,
397            _marker: core::marker::PhantomData,
398        }
399    }
400
401    /// Consumes this [`RawAttachmentMut`] and returns a [`&mut dyn Any`](Any)
402    /// view of the attachment with the same lifetime.
403    ///
404    /// The returned reference can be downcast using
405    /// `<dyn Any>::downcast_mut`.
406    #[inline]
407    pub fn into_attachment_as_any_mut(self) -> &'a mut (dyn Any + 'static) {
408        let vtable = self.as_ref().vtable();
409        // SAFETY:
410        // 1. The vtable returned by `self.as_ref().vtable()` is guaranteed to match the
411        //    data in the `AttachmentData`.
412        unsafe {
413            // @add-unsafe-context: AttachmentData
414            vtable.attachment_as_any_mut(self)
415        }
416    }
417
418    /// Consumes the mutable reference and returns an immutable one with the
419    /// same lifetime.
420    #[inline]
421    pub fn into_ref(self) -> RawAttachmentRef<'a> {
422        RawAttachmentRef {
423            // SAFETY:
424            // 1. The pointer comes from `Box::into_raw` (guaranteed by `self`'s invariant)
425            // 2. We are creating the `RawAttachmentRef` here, and we are not changing the pointer
426            // 3. The returned `RawAttachmentRef<'a>` represents shared access for lifetime `'a`,
427            //    which is valid because we are consuming `self` and turning it into a shared
428            //    reference
429            ptr: self.ptr,
430            _marker: core::marker::PhantomData,
431        }
432    }
433}
434
435#[cfg(test)]
436mod tests {
437    use alloc::string::String;
438
439    use super::*;
440    use crate::handlers::AttachmentHandler;
441
442    struct HandlerI32;
443    impl AttachmentHandler<i32> for HandlerI32 {
444        fn display(value: &i32, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
445            core::fmt::Display::fmt(value, formatter)
446        }
447
448        fn debug(value: &i32, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
449            core::fmt::Debug::fmt(value, formatter)
450        }
451    }
452
453    struct HandlerString;
454    impl AttachmentHandler<String> for HandlerString {
455        fn display(value: &String, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
456            core::fmt::Display::fmt(value, formatter)
457        }
458
459        fn debug(value: &String, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
460            core::fmt::Debug::fmt(value, formatter)
461        }
462    }
463
464    #[test]
465    fn test_raw_attachment_size() {
466        assert_eq!(
467            core::mem::size_of::<RawAttachment>(),
468            core::mem::size_of::<usize>()
469        );
470        assert_eq!(
471            core::mem::size_of::<Option<RawAttachment>>(),
472            core::mem::size_of::<usize>()
473        );
474        assert_eq!(
475            core::mem::size_of::<Result<(), RawAttachment>>(),
476            core::mem::size_of::<usize>()
477        );
478        assert_eq!(
479            core::mem::size_of::<Result<String, RawAttachment>>(),
480            core::mem::size_of::<String>()
481        );
482        assert_eq!(
483            core::mem::size_of::<Option<Option<RawAttachment>>>(),
484            core::mem::size_of::<Option<usize>>()
485        );
486
487        assert_eq!(
488            core::mem::size_of::<RawAttachmentRef<'_>>(),
489            core::mem::size_of::<usize>()
490        );
491        assert_eq!(
492            core::mem::size_of::<Option<RawAttachmentRef<'_>>>(),
493            core::mem::size_of::<usize>()
494        );
495        assert_eq!(
496            core::mem::size_of::<Result<(), RawAttachmentRef<'_>>>(),
497            core::mem::size_of::<usize>()
498        );
499        assert_eq!(
500            core::mem::size_of::<Result<String, RawAttachmentRef<'_>>>(),
501            core::mem::size_of::<String>()
502        );
503        assert_eq!(
504            core::mem::size_of::<Option<Option<RawAttachmentRef<'_>>>>(),
505            core::mem::size_of::<Option<usize>>()
506        );
507
508        assert_eq!(
509            core::mem::size_of::<RawAttachmentMut<'_>>(),
510            core::mem::size_of::<usize>()
511        );
512        assert_eq!(
513            core::mem::size_of::<Option<RawAttachmentMut<'_>>>(),
514            core::mem::size_of::<usize>()
515        );
516        assert_eq!(
517            core::mem::size_of::<Result<(), RawAttachmentMut<'_>>>(),
518            core::mem::size_of::<usize>()
519        );
520        assert_eq!(
521            core::mem::size_of::<Result<String, RawAttachmentMut<'_>>>(),
522            core::mem::size_of::<String>()
523        );
524        assert_eq!(
525            core::mem::size_of::<Option<Option<RawAttachmentMut<'_>>>>(),
526            core::mem::size_of::<Option<usize>>()
527        );
528    }
529
530    #[test]
531    fn test_raw_attachment_get_refs() {
532        let attachment = RawAttachment::new::<i32, HandlerI32>(100);
533        let attachment_ref = attachment.as_ref();
534
535        // Accessing the pointer multiple times should be safe and consistent
536        let ptr1 = attachment_ref.as_ptr();
537        let ptr2 = attachment_ref.as_ptr();
538        assert_eq!(ptr1, ptr2);
539    }
540
541    #[test]
542    fn test_raw_attachment_downcast() {
543        let int_attachment = RawAttachment::new::<i32, HandlerI32>(42);
544        let string_attachment = RawAttachment::new::<String, HandlerString>(String::from("test"));
545
546        let int_ref = int_attachment.as_ref();
547        let string_ref = string_attachment.as_ref();
548
549        // Are TypeIds what we expect?
550        assert_eq!(int_ref.attachment_type_id(), TypeId::of::<i32>());
551        assert_eq!(string_ref.attachment_type_id(), TypeId::of::<String>());
552
553        // The vtables should be different
554        assert!(!core::ptr::eq(int_ref.vtable(), string_ref.vtable()));
555    }
556
557    #[test]
558    fn test_raw_attachment_display_debug() {
559        use alloc::format;
560
561        let int_attachment = RawAttachment::new::<i32, HandlerI32>(42);
562        let string_attachment = RawAttachment::new::<String, HandlerString>(String::from("test"));
563
564        let int_ref = int_attachment.as_ref();
565        let string_ref = string_attachment.as_ref();
566
567        // Test display formatting
568        let display_int = format!(
569            "{}",
570            TestDisplayFormatter::new(|f| int_ref.attachment_display(f))
571        );
572        let display_string = format!(
573            "{}",
574            TestDisplayFormatter::new(|f| string_ref.attachment_display(f))
575        );
576
577        assert_eq!(display_int, "42");
578        assert_eq!(display_string, "test");
579
580        // Test debug formatting
581        let debug_int = format!(
582            "{}",
583            TestDisplayFormatter::new(|f| int_ref.attachment_debug(f))
584        );
585        let debug_string = format!(
586            "{}",
587            TestDisplayFormatter::new(|f| string_ref.attachment_debug(f))
588        );
589
590        assert_eq!(debug_int, "42");
591        assert_eq!(debug_string, "\"test\"");
592    }
593
594    // Helper struct for testing display/debug formatting
595    struct TestDisplayFormatter<F> {
596        formatter_fn: F,
597    }
598
599    impl<F> TestDisplayFormatter<F>
600    where
601        F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result,
602    {
603        fn new(formatter_fn: F) -> Self {
604            Self { formatter_fn }
605        }
606    }
607
608    impl<F> core::fmt::Display for TestDisplayFormatter<F>
609    where
610        F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result,
611    {
612        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
613            (self.formatter_fn)(f)
614        }
615    }
616
617    #[test]
618    fn test_send_sync() {
619        static_assertions::assert_not_impl_any!(RawAttachment: Send, Sync);
620        static_assertions::assert_not_impl_any!(RawAttachmentRef<'_>: Send, Sync);
621        static_assertions::assert_not_impl_any!(RawAttachmentMut<'_>: Send, Sync);
622    }
623
624    #[test]
625    fn test_raw_attachment_as_any() {
626        let attachment = RawAttachment::new::<i32, HandlerI32>(42);
627        let any = attachment.as_ref().attachment_as_any();
628        assert_eq!(any.downcast_ref::<i32>(), Some(&42));
629        assert!(any.downcast_ref::<String>().is_none());
630    }
631
632    #[test]
633    fn test_raw_attachment_as_any_mut() {
634        let mut attachment = RawAttachment::new::<i32, HandlerI32>(41);
635        let any = attachment.as_mut().into_attachment_as_any_mut();
636        *any.downcast_mut::<i32>().unwrap() += 1;
637        assert_eq!(
638            unsafe { *attachment.as_ref().attachment_downcast_unchecked::<i32>() },
639            42
640        );
641    }
642}