Macro castaway::cast[][src]

macro_rules! cast {
    ($value:expr, $T:ty) => { ... };
}
Expand description

Attempt to cast the result of an expression into a given concrete type. If the expression is in fact of the given type, an Ok is returned containing the result of the expression as that type. If the types do not match, the value is returned in an Err unchanged.

This macro is designed to work inside a generic context, and allows you to downcast generic types to their concrete types or to another generic type at compile time. If you are looking for the ability to downcast values at runtime, you should use Any instead.

This macro does not perform any sort of type conversion (such as re-interpreting i32 as u32 and so on), it only resolves generic types to concrete types if the instantiated generic type is exactly the same as the type you specify. If you are looking to reinterpret the bits of a value as a type other than the one it actually is, then you should look for a different library.

Invoking this macro is zero-cost, meaning after normal compiler optimization steps there will be no code generated in the final binary for performing a cast. In debug builds some glue code may be present with a small runtime cost.

Restrictions

Attempting to perform an illegal or unsupported cast that can never be successful, such as casting to a value with a longer lifetime than the expression, will produce a compile-time error.

Due to language limitations with lifetime bounds, this macro is more restrictive than what is theoretically possible and rejects some legal casts. This is to ensure safety and correctness around lifetime handling. Examples include the following:

  • Casting an expression by value with a non-'static lifetime is not allowed. For example, you cannot attempt to cast a T: 'a to Foo<'a>.
  • Casting to a reference with a non-'static lifetime is not allowed if the expression type is not required to be a reference. For example, you can attempt to cast a &T to &String, but you can’t attempt to cast a T to &String because T may or may not be a reference. You can, however, attempt to cast a T: 'static to &'static String.
  • You cannot cast references whose target itself may contain non-'static references. For example, you can attempt to cast a &'a T: 'static to &'a Foo<'static>, but you can’t attempt to cast a &'a T: 'b to &'a Foo<'b>.

Examples

Performing a trivial cast:

use castaway::cast;

let value: u8 = 0;
assert_eq!(cast!(value, u8), Ok(0));

Performing a cast in a generic context:

use castaway::cast;

fn is_this_a_u8<T: 'static>(value: T) -> bool {
    cast!(value, u8).is_ok()
}

assert!(is_this_a_u8(0u8));
assert!(!is_this_a_u8(0u16));

Specialization in a blanket trait implementation:

use std::fmt::Display;
use castaway::cast;

/// Like `std::string::ToString`, but with an optimization when `Self` is
/// already a `String`.
///
/// Since the standard library is allowed to use unstable features,
/// `ToString` already has this optimization using the `specialization`
/// feature, but this isn't something normal crates can do.
pub trait FastToString {
    fn fast_to_string(&self) -> String;
}

impl<T: Display + 'static> FastToString for T {
    fn fast_to_string<'local>(&'local self) -> String {
        // If `T` is already a string, then take a different code path.
        // After monomorphization, this check will be completely optimized
        // away.
        //
        // Note we can cast a `&'local self` to a `&'local String` as long
        // as both `Self` and `String` are `'static`.
        if let Ok(string) = cast!(self, &String) {
            // Don't invoke the std::fmt machinery, just clone the string.
            string.to_owned()
        } else {
            // Make use of `Display` for any other `T`.
            format!("{}", self)
        }
    }
}

println!("specialized: {}", String::from("hello").fast_to_string());
println!("default: {}", "hello".fast_to_string());