dynamic/
lib.rs

1#![cfg_attr(test, deny(warnings))]
2#![deny(missing_docs)]
3
4//! # dynamic
5//!
6//! A dynamically typed value with fast downcasting.
7//!
8//! Provides a `Dynamic` type, which contains a dynamically typed value.
9//! `Dynamic` is similar to `Any` from `std::any::Any`, except that downcasting
10//! does not involve any virtual calls since the `TypeId` of the contained
11//! value is pre-computed.
12
13extern crate unsafe_any as uany;
14
15use uany::UnsafeAnyExt;
16
17use std::any::{TypeId, Any};
18use std::{fmt, mem};
19
20/// A dynamically typed value.
21///
22/// Differs from `Any` in that it pre-computes type information at
23/// creation-time, so that downcasting and other queries to the type
24/// information can be implemented without virtual calls.
25///
26/// Not Sized, since the size of the type is determined at runtime, so must be
27/// used behind a pointer (e.g. `&Dynamic`, `Box<Dynamic>`, etc.)
28pub struct Dynamic {
29    id: TypeId,
30    data: Dyn
31}
32
33impl Dynamic {
34    /// Create a new, heap-allocated Dynamic value containing the given value.
35    ///
36    /// The resulting `Dynamic` can be downcasted back to a `T`.
37    #[inline]
38    pub fn new<T: Any>(val: T) -> Box<Dynamic> {
39        let un_sized = Box::new(Described {
40            id: TypeId::of::<T>(),
41            data: val
42        }) as Box<Described<Dyn>>;
43
44        unsafe { mem::transmute(un_sized) }
45    }
46
47    /// Create a new, immutable Dynamic value from the given described reference.
48    ///
49    /// The resulting `Dynamic` can be downcasted back to a `T`.
50    #[inline]
51    pub fn from_ref<T: Any>(val: &Described<T>) -> &Dynamic {
52        let un_sized = val as &Described<Dyn>;
53        unsafe { mem::transmute(un_sized) }
54    }
55
56    /// Create a new, mutable Dynamic value from the given described reference.
57    ///
58    /// The resulting `Dynamic` can be downcasted back to a `T`.
59    #[inline]
60    pub fn from_mut<T: Any>(val: &mut Described<T>) -> &mut Dynamic {
61        let un_sized = val as &mut Described<Dyn>;
62        unsafe { mem::transmute(un_sized) }
63    }
64
65    /// Read the type id for the contained value.
66    #[inline]
67    pub fn id(&self) -> TypeId { self.id }
68
69    /// Check if the contained type is a `T`.
70    #[inline(always)]
71    pub fn is<T: Any>(&self) -> bool {
72        self.id == TypeId::of::<T>()
73    }
74
75    /// If the contained value is a `T`, downcast back to it.
76    ///
77    /// If the value is not a `T`, returns `Err(self)`.
78    #[inline]
79    pub fn downcast<T: Any>(self: Box<Self>) -> Result<Box<Described<T>>, Box<Self>> {
80        if self.is::<T>() {
81            Ok(unsafe { Box::from_raw(Box::into_raw(self) as *mut Described<T>) })
82        } else {
83            Err(self)
84        }
85    }
86
87    /// If the contained value is a `T`, get an immutable reference to it.
88    #[inline]
89    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
90        if self.is::<T>() {
91            Some(unsafe { self.data.downcast_ref_unchecked() })
92        } else {
93            None
94        }
95    }
96
97    /// If the contained value is a `T`, get a mutable reference to it.
98    #[inline]
99    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
100        if self.is::<T>() {
101            Some(unsafe { self.data.downcast_mut_unchecked() })
102        } else {
103            None
104        }
105    }
106}
107
108impl fmt::Debug for Dynamic {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        f.debug_struct("Dynamic")
111            .field("id", &self.id)
112            .field("data", &"{{ dynamically typed value }}")
113            .finish()
114    }
115}
116
117/// A value T paired with its type descriptor.
118///
119/// Can be converted to a `Dynamic` value.
120#[derive(Copy, Clone, Debug, PartialEq)]
121pub struct Described<T: ?Sized> {
122    // The TypeId is private to prevent mutation, as a user could then
123    // invalidate it.
124    id: TypeId,
125
126    /// The described data.
127    pub data: T
128}
129
130impl<T: Any> Described<T> {
131    /// Create a new Described instance that can be converted to a Dynamic.
132    #[inline]
133    pub fn new(val: T) -> Described<T> {
134        Described {
135            id: TypeId::of::<T>(),
136            data: val
137        }
138    }
139
140    /// Read the type id for this value.
141    #[inline]
142    pub fn id(&self) -> TypeId { self.id }
143}
144
145// Empty trait for small vtables.
146trait Dyn {}
147impl<T> Dyn for T {}
148
149// Add raw downcasting methods to Dyn trait objects.
150unsafe impl UnsafeAnyExt for Dyn {}
151
152#[cfg(test)]
153mod test {
154    use std::any::TypeId;
155    use {Dynamic, Described};
156
157    struct X(usize);
158    struct Y(usize);
159    struct Z(usize);
160
161    #[test]
162    fn test_downcasting() {
163        let mut x = Dynamic::new(X(1));
164
165        assert!(x.is::<X>());
166        assert!(!x.is::<Y>());
167        assert!(!x.is::<Z>());
168
169        *x.downcast_mut::<X>().unwrap() = X(100);
170        assert_eq!(x.downcast_ref::<X>().unwrap().0, 100);
171
172        let described_x = x.downcast::<X>().unwrap();
173        assert_eq!(described_x.id(), TypeId::of::<X>());
174        assert_eq!(described_x.data.0, 100);
175    }
176
177    #[test]
178    fn test_dynamic_refs() {
179        let described_z = Described::new(Z(1000));
180
181        let z_ref = Dynamic::from_ref(&described_z);
182        assert_eq!(z_ref.downcast_ref::<Z>().unwrap().0, 1000);
183    }
184}
185