castaway/lib.rs
1//! Safe, zero-cost downcasting for limited compile-time specialization.
2//!
3//! This crate works fully on stable Rust, and also does not require the
4//! standard library. To disable references to the standard library, you must
5//! opt-out of the `std` feature using `default-features = false` in your
6//! `Cargo.toml` file. When in no-std mode, a separate `alloc` feature flag
7//! is available to support casting to several [`alloc`] types not included
8//! in [`core`].
9//!
10//! Castaway provides the following key macros:
11//!
12//! - [`cast`]: Attempt to cast the result of an expression into a given
13//! concrete type.
14//! - [`match_type`]: Match the result of an expression against multiple
15//! concrete types.
16
17#![no_std]
18
19#[cfg(feature = "std")]
20extern crate std;
21
22#[cfg(feature = "alloc")]
23extern crate alloc;
24
25#[doc(hidden)]
26pub mod internal;
27mod lifetime_free;
28mod utils;
29
30pub use lifetime_free::LifetimeFree;
31
32/// Attempt to cast the result of an expression into a given concrete type.
33///
34/// If the expression is in fact of the given type, an [`Ok`] is returned
35/// containing the result of the expression as that type. If the types do not
36/// match, the value is returned in an [`Err`] unchanged.
37///
38/// This macro is designed to work inside a generic context, and allows you to
39/// downcast generic types to their concrete types or to another generic type at
40/// compile time. If you are looking for the ability to downcast values at
41/// runtime, you should use [`Any`](core::any::Any) instead.
42///
43/// This macro does not perform any sort of type _conversion_ (such as
44/// re-interpreting `i32` as `u32` and so on), it only resolves generic types to
45/// concrete types if the instantiated generic type is exactly the same as the
46/// type you specify. If you are looking to reinterpret the bits of a value as a
47/// type other than the one it actually is, then you should look for a different
48/// library.
49///
50/// Invoking this macro is zero-cost, meaning after normal compiler optimization
51/// steps there will be no code generated in the final binary for performing a
52/// cast. In debug builds some glue code may be present with a small runtime
53/// cost.
54///
55/// # Restrictions
56///
57/// Attempting to perform an illegal or unsupported cast that can never be
58/// successful, such as casting to a value with a longer lifetime than the
59/// expression, will produce a compile-time error.
60///
61/// Due to language limitations with lifetime bounds, this macro is more
62/// restrictive than what is theoretically possible and rejects some legal
63/// casts. This is to ensure safety and correctness around lifetime handling.
64/// Examples include the following:
65///
66/// - Casting an expression by value with a non-`'static` lifetime is not
67/// allowed. For example, you cannot attempt to cast a `T: 'a` to `Foo<'a>`.
68/// - Casting to a reference with a non-`'static` lifetime is not allowed if the
69/// expression type is not required to be a reference. For example, you can
70/// attempt to cast a `&T` to `&String`, but you can't attempt to cast a `T`
71/// to `&String` because `T` may or may not be a reference. You can, however,
72/// attempt to cast a `T: 'static` to `&'static String`.
73/// - You cannot cast references whose target itself may contain non-`'static`
74/// references. For example, you can attempt to cast a `&'a T: 'static` to
75/// `&'a Foo<'static>`, but you can't attempt to cast a `&'a T: 'b` to `&'a
76/// Foo<'b>`.
77/// - You can cast generic slices as long as the item type is `'static` and
78/// `Sized`, but you cannot cast a generic reference to a slice or vice versa.
79///
80/// Some exceptions are made to the above restrictions for certain types which
81/// are known to be _lifetime-free_. You can cast a generic type to any
82/// lifetime-free type by value or by reference, even if the generic type is not
83/// `'static`.
84///
85/// A type is considered lifetime-free if it contains no generic lifetime
86/// bounds, ensuring that all possible instantiations of the type are always
87/// `'static`. To mark a type as being lifetime-free and enable it to be casted
88/// to in this manner by this macro it must implement the [`LifetimeFree`]
89/// trait. This is implemented automatically for all primitive types and for
90/// several [`core`] types. If you enable the `std` crate feature, then it will
91/// also be implemented for several [`std`] types as well. If you enable the
92/// `alloc` crate feature, then it will be implemented for several [`alloc`]
93/// types without linking to the standard library as the `std` feature would.
94///
95/// # Examples
96///
97/// The above restrictions are admittedly complex and can be tricky to reason
98/// about, so it is recommended to read the following examples to get a feel for
99/// what is, and what is not, supported.
100///
101/// Performing trivial casts:
102///
103/// ```
104/// use castaway::cast;
105///
106/// let value: u8 = 0;
107/// assert_eq!(cast!(value, u8), Ok(0));
108///
109/// let slice: &[u8] = &[value];
110/// assert_eq!(cast!(slice, &[u8]), Ok(slice));
111/// ```
112///
113/// Performing a cast in a generic context:
114///
115/// ```
116/// use castaway::cast;
117///
118/// fn is_this_a_u8<T: 'static>(value: T) -> bool {
119/// cast!(value, u8).is_ok()
120/// }
121///
122/// assert!(is_this_a_u8(0u8));
123/// assert!(!is_this_a_u8(0u16));
124///
125/// // Note that we can also implement this without the `'static` type bound
126/// // because the only type(s) we care about casting to all implement
127/// // `LifetimeFree`:
128///
129/// fn is_this_a_u8_non_static<T>(value: T) -> bool {
130/// cast!(value, u8).is_ok()
131/// }
132///
133/// assert!(is_this_a_u8_non_static(0u8));
134/// assert!(!is_this_a_u8_non_static(0u16));
135/// ```
136///
137/// Specialization in a blanket trait implementation:
138///
139/// ```
140/// # #[cfg(feature = "std")] {
141/// use std::fmt::Display;
142/// use castaway::cast;
143///
144/// /// Like `std::string::ToString`, but with an optimization when `Self` is
145/// /// already a `String`.
146/// ///
147/// /// Since the standard library is allowed to use unstable features,
148/// /// `ToString` already has this optimization using the `specialization`
149/// /// feature, but this isn't something normal crates can do.
150/// pub trait FastToString {
151/// fn fast_to_string(&self) -> String;
152/// }
153///
154/// impl<T: Display> FastToString for T {
155/// fn fast_to_string<'local>(&'local self) -> String {
156/// // If `T` is already a string, then take a different code path.
157/// // After monomorphization, this check will be completely optimized
158/// // away.
159/// //
160/// // Note we can cast a `&'local self` to a `&'local String` as `String`
161/// // implements `LifetimeFree`.
162/// if let Ok(string) = cast!(self, &String) {
163/// // Don't invoke the std::fmt machinery, just clone the string.
164/// string.to_owned()
165/// } else {
166/// // Make use of `Display` for any other `T`.
167/// format!("{}", self)
168/// }
169/// }
170/// }
171///
172/// println!("specialized: {}", String::from("hello").fast_to_string());
173/// println!("default: {}", "hello".fast_to_string());
174/// # }
175/// ```
176#[macro_export]
177macro_rules! cast {
178 ($value:expr, $T:ty) => {{
179 #[allow(unused_imports)]
180 use $crate::internal::*;
181
182 // Here we are using an _autoderef specialization_ technique, which
183 // exploits method resolution autoderefs to select different cast
184 // implementations based on the type of expression passed in. The traits
185 // imported above are all in scope and all have the potential to be
186 // chosen to resolve the method name `try_cast` based on their generic
187 // constraints.
188 //
189 // To support casting references with non-static lifetimes, the traits
190 // limited to reference types require less dereferencing to invoke and
191 // thus are preferred by the compiler if applicable.
192 let value = $value;
193 let src_token = CastToken::of_val(&value);
194 let dest_token = CastToken::<$T>::of();
195
196 // Note: The number of references added here must be kept in sync with
197 // the largest number of references used by any trait implementation in
198 // the internal module.
199 let result: ::core::result::Result<$T, _> = (&&&&&&&(src_token, dest_token)).try_cast(value);
200
201 result
202 }};
203
204 ($value:expr) => {
205 $crate::cast!($value, _)
206 };
207}
208
209/// Match the result of an expression against multiple concrete types.
210///
211/// You can write multiple match arms in the following syntax:
212///
213/// ```no_compile
214/// TYPE as name => { /* expression */ }
215/// ```
216///
217/// If the concrete type matches the given type, then the value will be cast to
218/// that type and bound to the given variable name. The expression on the
219/// right-hand side of the match is then executed and returned as the result of
220/// the entire match expression.
221///
222/// The name following the `as` keyword can be any [irrefutable
223/// pattern](https://doc.rust-lang.org/stable/reference/patterns.html#refutability).
224/// Like `match` or `let` expressions, you can use an underscore to prevent
225/// warnings if you don't use the casted value, such as `_value` or just `_`.
226///
227/// Since it would be impossible to exhaustively list all possible types of an
228/// expression, you **must** include a final default match arm. The default
229/// match arm does not specify a type:
230///
231/// ```no_compile
232/// name => { /* expression */ }
233/// ```
234///
235/// The original expression will be bound to the given variable name without
236/// being casted. If you don't care about the original value, the default arm
237/// can be:
238///
239/// ```no_compile
240/// _ => { /* expression */ }
241/// ```
242///
243/// This macro has all the same rules and restrictions around type casting as
244/// [`cast`].
245///
246/// # Examples
247///
248/// ```
249/// use std::fmt::Display;
250/// use castaway::match_type;
251///
252/// fn to_string<T: Display + 'static>(value: T) -> String {
253/// match_type!(value, {
254/// String as s => s,
255/// &str as s => s.to_string(),
256/// s => s.to_string(),
257/// })
258/// }
259///
260/// println!("{}", to_string("foo"));
261/// ```
262#[macro_export]
263macro_rules! match_type {
264 ($value:expr, {
265 $T:ty as $pat:pat => $branch:expr,
266 $($tail:tt)+
267 }) => {
268 match $crate::cast!($value, $T) {
269 Ok(value) => {
270 let $pat = value;
271 $branch
272 },
273 Err(value) => $crate::match_type!(value, {
274 $($tail)*
275 })
276 }
277 };
278
279 ($value:expr, {
280 $pat:pat => $branch:expr $(,)?
281 }) => {{
282 let $pat = $value;
283 $branch
284 }};
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[cfg(feature = "alloc")]
292 use alloc::string::String;
293
294 #[test]
295 fn cast() {
296 assert_eq!(cast!(0u8, u16), Err(0u8));
297 assert_eq!(cast!(1u8, u8), Ok(1u8));
298 assert_eq!(cast!(2u8, &'static u8), Err(2u8));
299 assert_eq!(cast!(2u8, &u8), Err(2u8)); // 'static is inferred
300
301 static VALUE: u8 = 2u8;
302 assert_eq!(cast!(&VALUE, &u8), Ok(&2u8));
303 assert_eq!(cast!(&VALUE, &'static u8), Ok(&2u8));
304 assert_eq!(cast!(&VALUE, &u16), Err(&2u8));
305 assert_eq!(cast!(&VALUE, &i8), Err(&2u8));
306
307 let value = 2u8;
308
309 fn inner<'a>(value: &'a u8) {
310 assert_eq!(cast!(value, &u8), Ok(&2u8));
311 assert_eq!(cast!(value, &'a u8), Ok(&2u8));
312 assert_eq!(cast!(value, &u16), Err(&2u8));
313 assert_eq!(cast!(value, &i8), Err(&2u8));
314 }
315
316 inner(&value);
317
318 let mut slice = [1u8; 2];
319
320 fn inner2<'a>(value: &'a [u8]) {
321 assert_eq!(cast!(value, &[u8]), Ok(&[1, 1][..]));
322 assert_eq!(cast!(value, &'a [u8]), Ok(&[1, 1][..]));
323 assert_eq!(cast!(value, &'a [u16]), Err(&[1, 1][..]));
324 assert_eq!(cast!(value, &'a [i8]), Err(&[1, 1][..]));
325 }
326
327 inner2(&slice);
328
329 #[allow(clippy::needless_lifetimes)]
330 fn inner3<'a>(value: &'a mut [u8]) {
331 assert_eq!(cast!(value, &mut [u8]), Ok(&mut [1, 1][..]));
332 }
333 inner3(&mut slice);
334 }
335
336 #[test]
337 fn cast_with_type_inference() {
338 let result: Result<u8, u8> = cast!(0u8);
339 assert_eq!(result, Ok(0u8));
340
341 let result: Result<u8, u16> = cast!(0u16);
342 assert_eq!(result, Err(0u16));
343 }
344
345 #[test]
346 fn match_type() {
347 let v = 42i32;
348
349 assert!(match_type!(v, {
350 u32 as _ => false,
351 i32 as _ => true,
352 _ => false,
353 }));
354 }
355
356 #[test]
357 fn cast_lifetime_free_unsized_ref() {
358 fn can_cast<T>(value: &[T]) -> bool {
359 cast!(value, &[u8]).is_ok()
360 }
361
362 let value = 42i32;
363 assert!(can_cast(&[1_u8, 2, 3, 4]));
364 assert!(!can_cast(&[1_i8, 2, 3, 4]));
365 assert!(!can_cast(&[&value, &value]));
366 }
367
368 #[test]
369 fn cast_lifetime_free_unsized_mut() {
370 fn can_cast<T>(value: &mut [T]) -> bool {
371 cast!(value, &mut [u8]).is_ok()
372 }
373
374 let value = 42i32;
375 assert!(can_cast(&mut [1_u8, 2, 3, 4]));
376 assert!(!can_cast(&mut [1_i8, 2, 3, 4]));
377 assert!(!can_cast(&mut [&value, &value]));
378 }
379
380 macro_rules! test_lifetime_free_cast {
381 () => {};
382
383 (
384 $(#[$meta:meta])*
385 for $TARGET:ty as $name:ident {
386 $(
387 $value:expr => $matches:pat $(if $guard:expr)?,
388 )+
389 }
390 $($tail:tt)*
391 ) => {
392 paste::paste! {
393 $(#[$meta])*
394 #[test]
395 #[allow(non_snake_case)]
396 fn [<cast_lifetime_free_ $name>]() {
397 fn do_cast<T>(value: T) -> Result<$TARGET, T> {
398 cast!(value, $TARGET)
399 }
400
401 $(
402 assert!(match do_cast($value) {
403 $matches $(if $guard)* => true,
404 _ => false,
405 });
406 )*
407 }
408
409 $(#[$meta])*
410 #[test]
411 #[allow(non_snake_case)]
412 fn [<cast_lifetime_free_ref_ $name>]() {
413 fn do_cast<T>(value: &T) -> Result<&$TARGET, &T> {
414 cast!(value, &$TARGET)
415 }
416
417 $(
418 assert!(match do_cast(&$value).map(|t| t.clone()).map_err(|e| e.clone()) {
419 $matches $(if $guard)* => true,
420 _ => false,
421 });
422 )*
423 }
424
425 $(#[$meta])*
426 #[test]
427 #[allow(non_snake_case)]
428 fn [<cast_lifetime_free_mut_ $name>]() {
429 fn do_cast<T>(value: &mut T) -> Result<&mut $TARGET, &mut T> {
430 cast!(value, &mut $TARGET)
431 }
432
433 $(
434 assert!(match do_cast(&mut $value).map(|t| t.clone()).map_err(|e| e.clone()) {
435 $matches $(if $guard)* => true,
436 _ => false,
437 });
438 )*
439 }
440 }
441
442 test_lifetime_free_cast! {
443 $($tail)*
444 }
445 };
446
447 (
448 $(#[$meta:meta])*
449 for $TARGET:ty {
450 $(
451 $value:expr => $matches:pat $(if $guard:expr)?,
452 )+
453 }
454 $($tail:tt)*
455 ) => {
456 paste::paste! {
457 $(#[$meta])*
458 #[test]
459 #[allow(non_snake_case)]
460 fn [<cast_lifetime_free_ $TARGET>]() {
461 fn do_cast<T>(value: T) -> Result<$TARGET, T> {
462 cast!(value, $TARGET)
463 }
464
465 $(
466 assert!(match do_cast($value) {
467 $matches $(if $guard)* => true,
468 _ => false,
469 });
470 )*
471 }
472
473 $(#[$meta])*
474 #[test]
475 #[allow(non_snake_case)]
476 fn [<cast_lifetime_free_ref_ $TARGET>]() {
477 fn do_cast<T>(value: &T) -> Result<&$TARGET, &T> {
478 cast!(value, &$TARGET)
479 }
480
481 $(
482 assert!(match do_cast(&$value).map(|t| t.clone()).map_err(|e| e.clone()) {
483 $matches $(if $guard)* => true,
484 _ => false,
485 });
486 )*
487 }
488
489 $(#[$meta])*
490 #[test]
491 #[allow(non_snake_case)]
492 fn [<cast_lifetime_free_mut_ $TARGET>]() {
493 fn do_cast<T>(value: &mut T) -> Result<&mut $TARGET, &mut T> {
494 cast!(value, &mut $TARGET)
495 }
496
497 $(
498 assert!(match do_cast(&mut $value).map(|t| t.clone()).map_err(|e| e.clone()) {
499 $matches $(if $guard)* => true,
500 _ => false,
501 });
502 )*
503 }
504 }
505
506 test_lifetime_free_cast! {
507 $($tail)*
508 }
509 };
510 }
511
512 test_lifetime_free_cast! {
513 for bool {
514 0u8 => Err(_),
515 true => Ok(true),
516 }
517
518 for u8 {
519 0u8 => Ok(0u8),
520 1u16 => Err(1u16),
521 42u8 => Ok(42u8),
522 }
523
524 for f32 {
525 3.2f32 => Ok(v) if v == 3.2,
526 3.2f64 => Err(v) if v == 3.2f64,
527 }
528
529 #[cfg(feature = "alloc")]
530 for String {
531 String::from("hello world") => Ok(ref v) if v.as_str() == "hello world",
532 "hello world" => Err("hello world"),
533 }
534
535 for Option<u8> as Option_u8 {
536 0u8 => Err(0u8),
537 Some(42u8) => Ok(Some(42u8)),
538 }
539
540 // See https://github.com/rust-lang/rust/issues/127286 for details.
541 for (u8, u8) as TupleU8U8 {
542 (1u8, 2u8) => Ok((1u8, 2u8)),
543 1u8 => Err(1u8),
544 }
545 for (bool, u16) as TupleBoolU16 {
546 (false, 2u16) => Ok((false, 2u16)),
547 true => Err(true),
548 }
549 }
550}