anymore/
lib.rs

1// Copyright 2025 the Anymore Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! The Anymore crate provides the [`AnyDebug`][] trait, for dynamically typed values which
5//! can be inspected in Rust.
6//!
7//! These can be used in any program domain, but have been developed with graphical
8//! user interface libraries as the target consumer.
9//! The `AnyDebug` trait also has the [`type_name`](AnyDebug::type_name) method, which
10//! can be used to access the type name of the contained value.
11//!
12//! ## Usage
13//!
14//! The traits in this crate can be used in the same way that you would use
15//! [`Any`], with the additional capability that the Debug implementation is meaningful.
16//! See the [module-level documentation][core::any] of `Any` for more details of how it
17//! can be used.
18//!
19//! ## Smart pointers and `dyn AnyDebug`
20//!
21//! When you have `dyn AnyDebug` contained in a smart pointer, such as [`Box`] or
22//! [`Arc`][alloc::sync::Arc],
23//! the [`type_name`][AnyDebug::type_name] method will give the type name of the smart
24//! pointer, rather than the type name of the contained value. This can be avoided by
25//! converting the smart pointer into a `&dyn AnyDebug` instead, which will return the
26//! object’s type name. This is the [same caveat][core::any#smart-pointers-and-dyn-any]
27//! seen with the`type_id` method on `Any`.
28//!
29//! ## Motivation
30//!
31//! In user interface contexts, there is often a need for passing dynamically typed values.
32//! In [Xilem](https://docs.rs/xilem/latest/xilem/), for example, one of the key concepts is
33//! dispatching arbitrary messages to the correct component (called `View` in `Xilem`).
34//! Each `View` expects messages of a specific type or types, and knows how to handle these.
35//! However, sometimes, due to bugs elsewhere in the app, a view will receive a message of a
36//! type it didn't expect. If it were using the standard library, `Any` type, there would be no
37//! feasible way for the author of that `View` implementation to learn what the underlying type was.
38//! This can make understanding this failure quite challenging.
39//! Because of this, Xilem uses the `AnyDebug` trait for these messages, so they can be inspected.
40//!
41//! A similar need arises in [Masonry](https://docs.rs/masonry/latest/masonry/), which is the widget toolkit
42//! co-developed with Xilem.
43//!
44//! ## Feature Flags
45//!
46//! The following crate [feature flags](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features) are available:
47//!
48//! - `alloc` (enabled by default): Implement downcasting from [`Box`]es.
49//!   If this feature is not enabled, Anymore can be used in contexts without an allocator enabled.
50//! - `type_name` (enabled by default): Provide the `type_name` function on `AnyDebug`, which gives the type's name.
51//!   Most users should leave this enabled, as the costs of this method existing are expected to be negligible.
52// LINEBENDER LINT SET - lib.rs - v3
53// See https://linebender.org/wiki/canonical-lints/
54// These lints shouldn't apply to examples or tests.
55#![cfg_attr(not(test), warn(unused_crate_dependencies))]
56// These lints shouldn't apply to examples.
57#![warn(clippy::print_stdout, clippy::print_stderr)]
58// Targeting e.g. 32-bit means structs containing usize can give false positives for 64-bit.
59#![cfg_attr(target_pointer_width = "64", warn(clippy::trivially_copy_pass_by_ref))]
60// END LINEBENDER LINT SET
61#![cfg_attr(docsrs, feature(doc_auto_cfg))]
62#![no_std]
63
64use core::any::Any;
65use core::fmt::Debug;
66
67#[cfg(feature = "alloc")]
68extern crate alloc;
69#[cfg(feature = "alloc")]
70use alloc::boxed::Box;
71
72/// A trait to implement dynamic typing.
73///
74/// This trait is the same as the standard library [`Any`] trait,
75/// except that it can be debug printed.
76///
77/// See also the [crate level documentation](crate) for more details.
78pub trait AnyDebug: Any + Debug {
79    /// Returns the [`type_name`](core::any::type_name) of this value's concrete type.
80    ///
81    /// This is useful for debugging downcasting failures.
82    /// In particular, in cases where you get `Box<Box<SomeType>>`
83    /// inadvertently converted into `Box<dyn AnyDebug>`, then attempting
84    /// to downcast to `SomeType` would fail.
85    ///
86    /// The `type_name` feature is provided for future-compatibility, to avoid needing a breaking release in case a user
87    /// did find that the `type_name` function were imposing an unacceptable binary size cost.
88    /// As this crate is intended for interop between crates, breaking releases are especially costly.
89    #[cfg(feature = "type_name")]
90    fn type_name(&self) -> &'static str;
91}
92
93impl<T: Any + Debug> AnyDebug for T {
94    #[cfg(feature = "type_name")]
95    fn type_name(&self) -> &'static str {
96        core::any::type_name::<Self>()
97    }
98}
99
100impl dyn AnyDebug {
101    /// Returns some shared reference to the inner value if it is of type `T`, or
102    /// `None` if it isn't.
103    ///
104    /// Forwards to the method defined on the type `dyn Any`.
105    pub fn downcast_ref<T: AnyDebug>(&self) -> Option<&T> {
106        (self as &dyn Any).downcast_ref::<T>()
107    }
108
109    /// Returns some exclusive reference to the inner value if it is of type `T`, or
110    /// `None` if it isn't.
111    ///
112    /// Forwards to the method defined on the type `dyn Any`.
113    pub fn downcast_mut<T: AnyDebug>(&mut self) -> Option<&mut T> {
114        (self as &mut dyn Any).downcast_mut::<T>()
115    }
116
117    /// Access the actual type of this [`AnyDebug`].
118    ///
119    /// Forwards to the method defined on the type [`Box<dyn Any>`].
120    ///
121    /// ## Errors
122    ///
123    /// If the message contained within `self` is not of type `T`, returns `self`.
124    #[cfg(feature = "alloc")]
125    pub fn downcast<T: AnyDebug>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
126        if self.is::<T>() {
127            Ok((self as Box<dyn Any>).downcast::<T>().unwrap())
128        } else {
129            Err(self)
130        }
131    }
132
133    /// Returns `true` if the inner type is the same as `T`.
134    ///
135    /// Forwards to the method defined on the type `dyn Any`.
136    pub fn is<T: AnyDebug>(&self) -> bool {
137        let this: &dyn Any = self;
138        this.is::<T>()
139    }
140}
141
142impl dyn AnyDebug + Send {
143    /// Returns some shared reference to the inner value if it is of type `T`, or
144    /// `None` if it isn't.
145    ///
146    /// Forwards to the method defined on the type `dyn Any`.
147    pub fn downcast_ref<T: AnyDebug>(&self) -> Option<&T> {
148        (self as &dyn Any).downcast_ref::<T>()
149    }
150
151    /// Returns some exclusive reference to the inner value if it is of type `T`, or
152    /// `None` if it isn't.
153    ///
154    /// Forwards to the method defined on the type `dyn Any`.
155    pub fn downcast_mut<T: AnyDebug>(&mut self) -> Option<&mut T> {
156        (self as &mut dyn Any).downcast_mut::<T>()
157    }
158
159    /// Access the actual type of this [`AnyDebug`].
160    ///
161    /// Forwards to the method defined on the type [`Box<dyn Any>`].
162    ///
163    /// ## Errors
164    ///
165    /// If the message contained within `self` is not of type `T`, returns `self`.
166    #[cfg(feature = "alloc")]
167    pub fn downcast<T: AnyDebug>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
168        if self.is::<T>() {
169            Ok((self as Box<dyn Any>).downcast::<T>().unwrap())
170        } else {
171            Err(self)
172        }
173    }
174
175    /// Returns `true` if the inner type is the same as `T`.
176    ///
177    /// Forwards to the method defined on the type `dyn Any`.
178    pub fn is<T: AnyDebug>(&self) -> bool {
179        let this: &dyn Any = self;
180        this.is::<T>()
181    }
182}
183
184impl dyn AnyDebug + Send + Sync {
185    /// Returns some shared reference to the inner value if it is of type `T`, or
186    /// `None` if it isn't.
187    ///
188    /// Forwards to the method defined on the type `dyn Any`.
189    pub fn downcast_ref<T: AnyDebug>(&self) -> Option<&T> {
190        (self as &dyn Any).downcast_ref::<T>()
191    }
192
193    /// Returns some exclusive reference to the inner value if it is of type `T`, or
194    /// `None` if it isn't.
195    ///
196    /// Forwards to the method defined on the type `dyn Any`.
197    pub fn downcast_mut<T: AnyDebug>(&mut self) -> Option<&mut T> {
198        (self as &mut dyn Any).downcast_mut::<T>()
199    }
200
201    /// Access the actual type of this [`AnyDebug`].
202    ///
203    /// Forwards to the method defined on the type [`Box<dyn Any>`].
204    ///
205    /// ## Errors
206    ///
207    /// If the message contained within `self` is not of type `T`, returns `self`.
208    #[cfg(feature = "alloc")]
209    pub fn downcast<T: AnyDebug>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
210        if self.is::<T>() {
211            Ok((self as Box<dyn Any>).downcast::<T>().unwrap())
212        } else {
213            Err(self)
214        }
215    }
216
217    /// Returns `true` if the inner type is the same as `T`.
218    ///
219    /// Forwards to the method defined on the type `dyn Any`.
220    pub fn is<T: AnyDebug>(&self) -> bool {
221        let this: &dyn Any = self;
222        this.is::<T>()
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    extern crate alloc;
229    use crate::AnyDebug;
230    use alloc::{boxed::Box, format};
231
232    #[derive(Debug)]
233    struct SomeMessage(u32);
234
235    #[test]
236    #[cfg(feature = "type_name")]
237    fn any_debug_correct_typename() {
238        let val = SomeMessage(4);
239        let val: &dyn AnyDebug = &val;
240        assert!(val.type_name().contains("SomeMessage"));
241    }
242
243    #[test]
244    fn any_debug_shared_correct_debug() {
245        let val = SomeMessage(5);
246        let val: &dyn AnyDebug = &val;
247        let format_result = format!("{val:?}");
248        assert!(format_result.contains("SomeMessage"));
249        assert!(format_result.contains("5"));
250    }
251    #[test]
252    fn any_debug_excl_correct_debug() {
253        let mut val = SomeMessage(6);
254        let val: &mut dyn AnyDebug = &mut val;
255        let format_result = format!("{val:?}");
256        assert!(format_result.contains("SomeMessage"));
257        assert!(format_result.contains("6"));
258    }
259    #[test]
260    fn any_debug_box_correct_debug() {
261        let val = SomeMessage(7);
262        let val: Box<dyn AnyDebug> = Box::new(val);
263        let format_result = format!("{val:?}");
264        assert!(format_result.contains("SomeMessage"));
265        assert!(format_result.contains("7"));
266    }
267
268    #[test]
269    fn any_debug_normal_is() {
270        let val = SomeMessage(10);
271        let val: &dyn AnyDebug = &val;
272        assert!(val.is::<SomeMessage>());
273        assert!(!val.is::<u32>());
274    }
275    #[test]
276    fn any_debug_normal_downcast_ref() {
277        let val = SomeMessage(11);
278        let val: &dyn AnyDebug = &val;
279        assert_eq!(val.downcast_ref::<SomeMessage>().unwrap().0, 11);
280    }
281    #[test]
282    fn any_debug_normal_downcast_mut() {
283        let mut val = SomeMessage(12);
284        let val_mut: &mut dyn AnyDebug = &mut val;
285        val_mut.downcast_mut::<SomeMessage>().unwrap().0 = 13;
286        assert!(val_mut.downcast_mut::<u32>().is_none());
287        assert_eq!(val.0, 13);
288    }
289
290    #[test]
291    #[cfg(feature = "alloc")]
292    fn any_debug_normal_downcast() {
293        let val = SomeMessage(14);
294        let val: Box<dyn AnyDebug> = Box::new(val);
295        let val = val.downcast::<u32>().unwrap_err();
296        let val = val.downcast::<SomeMessage>().unwrap();
297        assert_eq!(val.0, 14);
298    }
299
300    #[test]
301    fn any_debug_send_is() {
302        let val = SomeMessage(20);
303        let val: &(dyn AnyDebug + Send) = &val;
304        assert!(val.is::<SomeMessage>());
305        assert!(!val.is::<u32>());
306    }
307    #[test]
308    fn any_debug_send_downcast_ref() {
309        let val = SomeMessage(21);
310        let val: &(dyn AnyDebug + Send) = &val;
311        assert_eq!(val.downcast_ref::<SomeMessage>().unwrap().0, 21);
312    }
313    #[test]
314    fn any_debug_send_downcast_mut() {
315        let mut val = SomeMessage(22);
316        let val_mut: &mut (dyn AnyDebug + Send) = &mut val;
317        val_mut.downcast_mut::<SomeMessage>().unwrap().0 = 23;
318        assert!(val_mut.downcast_mut::<u32>().is_none());
319        assert_eq!(val.0, 23);
320    }
321    #[test]
322    #[cfg(feature = "alloc")]
323    fn any_debug_send_downcast() {
324        let val = SomeMessage(24);
325        let val: Box<(dyn AnyDebug + Send)> = Box::new(val);
326        let val = val.downcast::<u32>().unwrap_err();
327        let val = val.downcast::<SomeMessage>().unwrap();
328        assert_eq!(val.0, 24);
329    }
330
331    #[test]
332    fn any_debug_send_sync_is() {
333        let val = SomeMessage(30);
334        let val: &(dyn AnyDebug + Send + Sync) = &val;
335        assert!(val.is::<SomeMessage>());
336        assert!(!val.is::<u32>());
337    }
338    #[test]
339    fn any_debug_send_sync_downcast_ref() {
340        let val = SomeMessage(31);
341        let val: &(dyn AnyDebug + Send + Sync) = &val;
342        assert_eq!(val.downcast_ref::<SomeMessage>().unwrap().0, 31);
343    }
344    #[test]
345    fn any_debug_send_sync_downcast_mut() {
346        let mut val = SomeMessage(32);
347        let val_mut: &mut (dyn AnyDebug + Send + Sync) = &mut val;
348        val_mut.downcast_mut::<SomeMessage>().unwrap().0 = 33;
349        assert!(val_mut.downcast_mut::<u32>().is_none());
350        assert_eq!(val.0, 33);
351    }
352    #[test]
353    #[cfg(feature = "alloc")]
354    fn any_debug_send_sync_downcast() {
355        let val = SomeMessage(34);
356        let val: Box<(dyn AnyDebug + Send + Sync)> = Box::new(val);
357        let val = val.downcast::<u32>().unwrap_err();
358        let val = val.downcast::<SomeMessage>().unwrap();
359        assert_eq!(val.0, 34);
360    }
361}