dyn_dyn/lib.rs
1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![warn(missing_docs)]
4#![warn(clippy::undocumented_unsafe_blocks)]
5#![allow(clippy::needless_borrowed_reference)]
6#![forbid(unsafe_op_in_unsafe_fn)]
7#![feature(coerce_unsized)]
8#![feature(const_nonnull_new)]
9#![feature(const_option)]
10#![feature(const_trait_impl)]
11#![feature(const_type_id)]
12#![cfg_attr(feature = "dynamic-names", feature(const_type_name))]
13#![feature(doc_auto_cfg)]
14#![feature(ptr_metadata)]
15#![feature(unsize)]
16
17#[cfg(feature = "alloc")]
18extern crate alloc;
19
20mod cast_target;
21mod fat;
22mod table;
23
24#[doc(hidden)]
25pub mod internal;
26
27/// Declares a trait as being a base trait for downcasting.
28///
29/// This macro marks a trait as being a base for dynamic trait object downcasting. All `impl` blocks for this trait will need to use the
30/// [`#[dyn_dyn_impl]`](dyn_dyn_impl) attribute to declare what traits they wish to expose.
31pub use dyn_dyn_macros::dyn_dyn_base;
32
33/// Performs a dynamic downcast of a reference to a trait object where the trait was declared with [`#[dyn_dyn_base]`](dyn_dyn_base).
34///
35/// This macro allows for trying to cast such a reference to a reference to another trait object, returning an [`Option`] containing the
36/// reference to the downcast trait object if the object in question implements that trait.
37///
38/// This macro accepts the following types for a given base trait `B`, with the first matching set of conditions determining how the
39/// dereference will occur:
40///
41/// - A (mutable) reference to a type that implements `B`, returning a (mutable) reference referring to the same object as the original
42/// reference
43/// - A (mutable) reference to a pointer type that implements [`DynDyn<B>`], returning a (mutable) reference referring to the pointee of
44/// that pointer
45/// - A (mutable) reference to a pointer type that implements Deref with a target that implements `B`, returning a (mutable) reference
46/// referring to the pointee of that pointer
47///
48/// # Examples
49///
50/// ```rust
51/// # use dyn_dyn::{dyn_dyn_base, dyn_dyn_cast, dyn_dyn_impl};
52/// #[dyn_dyn_base]
53/// trait Base {}
54/// trait Trait {}
55///
56/// struct Struct;
57///
58/// #[dyn_dyn_impl(Trait)]
59/// impl Base for Struct {}
60/// impl Trait for Struct {}
61///
62/// fn downcast(r: &dyn Base) -> Result<&dyn Trait, &dyn Base> {
63/// dyn_dyn_cast!(Base => Trait, r)
64/// }
65///
66/// fn downcast_mut(r: &mut dyn Base) -> Result<&mut dyn Trait, &mut dyn Base> {
67/// dyn_dyn_cast!(mut Base => Trait, r)
68/// }
69///
70/// # #[cfg(feature = "alloc")]
71/// fn downcast_box(r: Box<dyn Base>) -> Result<Box<dyn Trait>, Box<dyn Base>> {
72/// dyn_dyn_cast!(move Base => Trait, r)
73/// }
74///
75/// fn main() {
76/// let mut s = Struct;
77///
78/// assert!(downcast(&s).is_ok());
79/// assert!(downcast_mut(&mut s).is_ok());
80/// # #[cfg(feature = "alloc")]
81/// assert!(downcast_box(Box::new(s)).is_ok());
82/// }
83/// ```
84pub use dyn_dyn_macros::dyn_dyn_cast;
85
86/// Marks an `impl` block as targeting a trait that was declared with the [`#[dyn_dyn_base]`](dyn_dyn_base) attribute.
87///
88/// This attribute allows the `impl` block to specify what other traits should be exposed for downcasting via the base trait that's being
89/// implemented in this block.
90///
91/// # Examples
92///
93/// ```rust
94/// # use core::fmt::Debug;
95/// # use dyn_dyn::{dyn_dyn_base, dyn_dyn_impl};
96/// #[dyn_dyn_base]
97/// trait Base {}
98///
99/// #[derive(Debug)]
100/// struct Struct;
101///
102/// #[dyn_dyn_impl(Debug)]
103/// impl Base for Struct {}
104/// ```
105///
106/// ```rust
107/// # use dyn_dyn::{dyn_dyn_base, dyn_dyn_impl};
108/// #[dyn_dyn_base]
109/// trait Base {}
110/// trait Trait<T> {}
111///
112/// struct Struct<T>(T);
113///
114/// impl<T> Trait<T> for Struct<T> {}
115///
116/// #[dyn_dyn_impl(Trait<T>)]
117/// impl<T: 'static> Base for Struct<T> {}
118/// ```
119pub use dyn_dyn_macros::dyn_dyn_impl;
120
121pub use cast_target::DynDynCastTarget;
122pub use fat::DynDynFat;
123pub use table::{AnyDynMetadata, DynDynTable, DynDynTableEntry, DynDynTableIterator};
124
125#[cfg(doc)]
126use core::ops::Deref;
127
128use cfg_if::cfg_if;
129use core::marker::{PhantomData, Unsize};
130use core::ops::DerefMut;
131use core::ptr::{self, Pointee};
132use stable_deref_trait::StableDeref;
133
134/// A type that can be dynamically downcast to other traits using the [`dyn_dyn_cast!`] macro.
135///
136/// This trait should not be manually implemented by user code. Instead, this trait should be implemented by using the
137/// [`#[dyn_dyn_base]`](dyn_dyn_base) attribute on the trait in question. The exact shape of this trait is subject to change at any time, so
138/// it generally shouldn't be relied upon in external code except as a trait bound.
139///
140/// # Safety
141///
142/// The result of calling [`DynDynBase::get_dyn_dyn_table`] on an object through a given base must never change for the lifetime of that
143/// object, even if the object itself is mutated.
144pub unsafe trait DynDynBase {
145 /// Gets the [`DynDynTable`] for this object, for traits exposed via this base trait.
146 ///
147 /// In user code, it is generally preferred to use the implementation of [`GetDynDynTable`] for references rather than calling this
148 /// method directly to avoid potential future breakage.
149 fn get_dyn_dyn_table(&self) -> DynDynTable;
150}
151
152/// Wraps a reference to a pointer implementing [`GetDynDynTable<B>`] and which can be dereferenced to perform the downcast.
153///
154/// Using [`dyn_dyn_cast!`] on this struct will call [`GetDynDynTable::get_dyn_dyn_table`] on the pointer itself, then dereference this
155/// pointer to perform the downcast. This allows a pointer implementing [`DynDyn<B>`] to be downcast into a reference without moving the
156/// pointer itself.
157pub struct DynDynRef<'a, B: ?Sized + DynDynBase, T: GetDynDynTable<B> + StableDeref>(
158 &'a T,
159 PhantomData<fn(B) -> B>,
160);
161
162impl<'a, B: ?Sized + DynDynBase, T: GetDynDynTable<B> + StableDeref> DynDynRef<'a, B, T>
163where
164 T::Target: Unsize<B>,
165{
166 /// Creates a new [`DynDynRef`] for the provided reference to a pointer.
167 pub fn new(r: &'a T) -> Self {
168 DynDynRef(r, PhantomData)
169 }
170}
171
172/// Wraps a mutable reference to a pointer implementing [`GetDynDynTable<B>`] and which can be dereferenced to perform the downcast.
173///
174/// Using [`dyn_dyn_cast!`] on this struct will call [`GetDynDynTable::get_dyn_dyn_table`] on the pointer itself, then dereference this
175/// pointer to perform the downcast. This allows a pointer implementing [`DynDyn<B>`] to be downcast into a mutable reference without moving
176/// the pointer itself.
177pub struct DynDynRefMut<'a, B: ?Sized + DynDynBase, T: GetDynDynTable<B> + StableDeref + DerefMut>(
178 &'a mut T,
179 PhantomData<fn(B) -> B>,
180);
181
182impl<'a, B: ?Sized + DynDynBase, T: GetDynDynTable<B> + StableDeref + DerefMut>
183 DynDynRefMut<'a, B, T>
184{
185 /// Creates a new [`DynDynRefMut`] for the provided mutable reference to a pointer.
186 pub fn new(r: &'a mut T) -> Self {
187 DynDynRefMut(r, PhantomData)
188 }
189}
190
191/// A pointer to an object which has a [`DynDynTable`] associated with it.
192///
193/// # Safety
194///
195/// - If this type implements [`Deref`], then the reference returned by calling [`Deref::deref`] must not change for the lifetime of this
196/// pointer unless the pointer itself is mutated.
197/// - If this type implements [`DerefMut`], then the reference returned by calling [`DerefMut::deref_mut`] must not change for the lifetime
198/// of this pointer unless the pointer itself is mutated and must point to the same object as a reference returned by calling
199/// [`Deref::deref`], including having identical metadata. Additionally, calling [`DerefMut::deref_mut`] must not mutate the pointer.
200/// - If this type implements [`Deref`], then the reference returned by calling [`Deref::deref`] must be unsize-coercible to a reference to
201/// [`GetDynDynTable::get_dyn_dyn_table`].
202/// - If this type implements [`Deref`], then the returned table must be equivalent to calling [`GetDynDynTable::get_dyn_dyn_table`] on a
203/// reference returned by calling [`Deref::deref`].
204/// - If this type implements [`DowncastUnchecked`], then the result of calling [`DowncastUnchecked::downcast_unchecked`] with
205/// metadata retrieved from the table returned by calling [`GetDynDynTable::get_dyn_dyn_table`] on this pointer shall be valid and safe to
206/// use.
207pub unsafe trait GetDynDynTable<B: ?Sized + DynDynBase> {
208 /// The actual type that this pointer currently points to. This type is used to allow propagation of auto trait bounds such as `Send`
209 /// and `Sync` in the `dyn_dyn_cast!` macro.
210 type DynTarget: ?Sized + Unsize<B>;
211
212 /// Gets the [`DynDynTable`] for the object that this pointer points to.
213 fn get_dyn_dyn_table(&self) -> DynDynTable;
214}
215
216/// A pointer to an object that can be unsafely downcast to point to another type.
217pub trait DowncastUnchecked<'a> {
218 /// The result of downcasting this pointer to point to the type `D`. Note that this type need not have the same outer wrapper as the
219 /// type implementing `DowncastUnchecked`, since the result of the downcast may involve coercions and dereferences.
220 type DowncastResult<D: ?Sized + 'a>;
221
222 /// Downcasts this pointer into a new pointer pointing to the same object, but having type `D`.
223 ///
224 /// Generally, the result of calling this function should be equivalent to turning this pointer type into a raw pointer, removing its
225 /// metadata, unsafely casting that pointer into a pointer to `D` using the provided metadata, and then turning that raw pointer into
226 /// another pointer type.
227 ///
228 /// As long as the concrete type of the pointee matches the concrete type of the metadata provided, then this is guaranteed to result
229 /// in a pointer which is valid and safe to use.
230 ///
231 /// # Safety
232 ///
233 /// Attaching the provided metadata to a pointer to the same data address as that held by this pointer must be guaranteed to be valid
234 /// and safe to use before this function can be called.
235 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(
236 self,
237 metadata: <D as Pointee>::Metadata,
238 ) -> Self::DowncastResult<D>;
239}
240
241/// A pointer object that can be safely downcast to refer to other trait types by using the `dyn_dyn_cast!` macro.
242pub trait DynDyn<'a, B: ?Sized + DynDynBase>: GetDynDynTable<B> + DowncastUnchecked<'a> {}
243
244impl<'a, B: ?Sized + DynDynBase, T: GetDynDynTable<B> + DowncastUnchecked<'a>> DynDyn<'a, B> for T {}
245
246// SAFETY: The referent of a shared reference will never change unexpectedly and the table returned matches that returned by dereferencing
247// it by definition. The DowncastUnchecked implementation is also a simple cast via converting to/from a pointer and so should be
248// correct.
249unsafe impl<'a, B: ?Sized + DynDynBase, T: ?Sized + Unsize<B>> GetDynDynTable<B> for &'a T {
250 type DynTarget = T;
251
252 fn get_dyn_dyn_table(&self) -> DynDynTable {
253 B::get_dyn_dyn_table(*self)
254 }
255}
256
257impl<'a, T: ?Sized> DowncastUnchecked<'a> for &'a T {
258 type DowncastResult<D: ?Sized + 'a> = &'a D;
259
260 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(
261 self,
262 metadata: <D as Pointee>::Metadata,
263 ) -> &'a D {
264 // SAFETY: Safety invariants for this fn require that the provided metadata is valid for self. Since the input reference has the
265 // lifetime 'a and the returned reference also has lifetime 'a, this dereference does not extend the reference's lifetime
266 // and only serves to re-attach the metadata.
267 unsafe { &*ptr::from_raw_parts(self as *const T as *const (), metadata) }
268 }
269}
270
271// SAFETY: Since T is StableDeref, the results of its Deref implementation should meet the stability requirements and the table returned is
272// simply passed through from T's GetDynDynTable<B> implementation, which is unsafe itself and can be assumed to be correct. The
273// DowncastUnchecked implementation defers to the impl for &T::Target, so it should be correct.
274unsafe impl<'a, B: ?Sized + DynDynBase, T: GetDynDynTable<B> + StableDeref + 'a> GetDynDynTable<B>
275 for DynDynRef<'a, B, T>
276where
277 T::Target: Unsize<B>,
278{
279 type DynTarget = T::DynTarget;
280
281 fn get_dyn_dyn_table(&self) -> DynDynTable {
282 <T as GetDynDynTable<B>>::get_dyn_dyn_table(self.0)
283 }
284}
285
286impl<'a, B: ?Sized + DynDynBase, T: DynDyn<'a, B> + StableDeref + 'a> DowncastUnchecked<'a>
287 for DynDynRef<'a, B, T>
288where
289 T::Target: Unsize<B>,
290{
291 type DowncastResult<D: ?Sized + 'a> = &'a D;
292
293 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(
294 self,
295 metadata: <D as Pointee>::Metadata,
296 ) -> Self::DowncastResult<D> {
297 // SAFETY: Just passing through to the implementation for &'a T.
298 unsafe { <&T::Target as DowncastUnchecked>::downcast_unchecked(&**self.0, metadata) }
299 }
300}
301
302// SAFETY: The referent of a mutable reference will never change unexpectedly and the table is returned by deferring to &T's implementation
303// and so should be correct. The DowncastUnchecked implementation is also a simple cast via converting to/from a pointer and so
304// should also be correct.
305unsafe impl<'a, B: ?Sized + DynDynBase, T: ?Sized + Unsize<B>> GetDynDynTable<B> for &'a mut T {
306 type DynTarget = T;
307
308 fn get_dyn_dyn_table(&self) -> DynDynTable {
309 B::get_dyn_dyn_table(*self)
310 }
311}
312
313impl<'a, T: ?Sized> DowncastUnchecked<'a> for &'a mut T {
314 type DowncastResult<D: ?Sized + 'a> = &'a mut D;
315
316 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(
317 self,
318 metadata: <D as Pointee>::Metadata,
319 ) -> &'a mut D {
320 // SAFETY: Safety invariants for this fn require that the provided metadata is valid for self. Since the input reference has the
321 // lifetime 'a and the returned reference also has lifetime 'a, this dereference does not extend the reference's lifetime
322 // and only serves to re-attach the metadata.
323 unsafe { &mut *ptr::from_raw_parts_mut(self as *mut T as *mut (), metadata) }
324 }
325}
326
327// SAFETY: Since T is StableDeref, the results of its Deref and DerefMut implementations should meet the stability requirements and the
328// table returned is simply passed through from T's GetDynDynTable<B> implementation, which is unsafe itself and can be assumed to
329// be correct. The DowncastUnchecked implementation defers to the impl for &mut T::Target, so it should be correct.
330unsafe impl<'a, B: ?Sized + DynDynBase, T: DynDyn<'a, B> + StableDeref + DerefMut + 'a>
331 GetDynDynTable<B> for DynDynRefMut<'a, B, T>
332where
333 T::Target: Unsize<B>,
334{
335 type DynTarget = T::Target;
336
337 fn get_dyn_dyn_table(&self) -> DynDynTable {
338 <T as GetDynDynTable<B>>::get_dyn_dyn_table(self.0)
339 }
340}
341
342impl<'a, B: ?Sized + DynDynBase, T: DynDyn<'a, B> + StableDeref + DerefMut + 'a>
343 DowncastUnchecked<'a> for DynDynRefMut<'a, B, T>
344where
345 T::Target: Unsize<B>,
346{
347 type DowncastResult<D: ?Sized + 'a> = &'a mut D;
348
349 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(
350 self,
351 metadata: <D as Pointee>::Metadata,
352 ) -> Self::DowncastResult<D> {
353 // SAFETY: Just passing through to the implementation for &'a mut T.
354 unsafe {
355 <&mut T::Target as DowncastUnchecked>::downcast_unchecked(&mut **self.0, metadata)
356 }
357 }
358}
359
360cfg_if! {
361 if #[cfg(feature = "alloc")] {
362 use alloc::boxed::Box;
363 use alloc::sync::Arc;
364 use alloc::rc::Rc;
365
366 // SAFETY: Box<T> meets all Deref/DerefMut stability requirements and the table is retrieved by dereferencing it, which is correct
367 // by definition. The DowncastUnchecked implementation is also a simple cast via converting to/from a pointer and so should
368 // be correct.
369 unsafe impl<B: ?Sized + DynDynBase, T: ?Sized + Unsize<B>> GetDynDynTable<B> for Box<T> {
370 type DynTarget = T;
371
372 fn get_dyn_dyn_table(&self) -> DynDynTable {
373 B::get_dyn_dyn_table(&**self)
374 }
375 }
376
377 impl<'a, T: ?Sized + 'a> DowncastUnchecked<'a>
378 for Box<T>
379 {
380 type DowncastResult<D: ?Sized + 'a> = Box<D>;
381
382 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(self, metadata: <D as Pointee>::Metadata) -> Box<D> {
383 // SAFETY: 1) NonNull::new_unchecked is fine since the raw pointer of a Box can never be null.
384 // 2) Box::from_raw is fine since the fat pointer passed in has the same data pointer as what we got from
385 // Box::into_raw and the metadata pointer is guaranteed to be valid by this fn's safety invariants.
386 unsafe {
387 Box::from_raw(
388 ptr::from_raw_parts_mut(Box::into_raw(self) as *mut (), metadata)
389 )
390 }
391 }
392 }
393
394 // SAFETY: Rc<T> meets all Deref/DerefMut stability requirements and the table is retrieved by dereferencing it, which is correct by
395 // definition. The DowncastUnchecked implementation is also a simple cast via converting to/from a pointer and so should be
396 // correct.
397 unsafe impl<B: ?Sized + DynDynBase, T: ?Sized + Unsize<B>> GetDynDynTable<B> for Rc<T> {
398 type DynTarget = T;
399
400 fn get_dyn_dyn_table(&self) -> DynDynTable {
401 B::get_dyn_dyn_table(&**self)
402 }
403 }
404
405 impl<'a, T: ?Sized + 'a> DowncastUnchecked<'a>
406 for Rc<T>
407 {
408 type DowncastResult<D: ?Sized + 'a> = Rc<D>;
409
410 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(self, metadata: <D as Pointee>::Metadata) -> Rc<D> {
411 // SAFETY: 1) NonNull::new_unchecked is fine since the raw pointer of a Box can never be null.
412 // 2) Rc::from_raw is fine since the fat pointer passed in has the same data pointer as what we got from
413 // Rc::into_raw and the metadata pointer is guaranteed to be valid by this fn's safety invariants.
414 unsafe {
415 Rc::from_raw(
416 ptr::from_raw_parts(
417 Rc::into_raw(self) as *const (),
418 metadata,
419 ),
420 )
421 }
422 }
423 }
424
425 // SAFETY: Arc<T> meets all Deref/DerefMut stability requirements and the table is retrieved by dereferencing it, which is correct
426 // by definition. The DowncastUnchecked implementation is also a simple cast via converting to/from a pointer and so should
427 // be correct.
428 unsafe impl<B: ?Sized + DynDynBase, T: ?Sized + Unsize<B>> GetDynDynTable<B> for Arc<T> {
429 type DynTarget = T;
430
431 fn get_dyn_dyn_table(&self) -> DynDynTable {
432 B::get_dyn_dyn_table(&**self)
433 }
434 }
435
436 impl<'a, T: ?Sized + 'a> DowncastUnchecked<'a>
437 for Arc<T>
438 {
439 type DowncastResult<D: ?Sized + 'a> = Arc<D>;
440
441 unsafe fn downcast_unchecked<D: ?Sized + Pointee>(self, metadata: <D as Pointee>::Metadata) -> Arc<D> {
442 // SAFETY: 1) NonNull::new_unchecked is fine since the raw pointer of a Box can never be null.
443 // 2) Arc::from_raw is fine since the fat pointer passed in has the same data pointer as what we got from
444 // Arc::into_raw and the metadata pointer is guaranteed to be valid by this fn's safety invariants.
445 unsafe {
446 Arc::from_raw(
447 ptr::from_raw_parts(Arc::into_raw(self) as *const (), metadata),
448 )
449 }
450 }
451 }
452 }
453}