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}