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}