TrySpecialize

Trait TrySpecialize 

Source
pub trait TrySpecialize {
    // Provided methods
    fn try_specialize<T>(self) -> Result<T, Self>
       where Self: Sized,
             T: LifetimeFree { ... }
    fn try_specialize_from<T>(other: T) -> Result<Self, T>
       where Self: Sized,
             T: LifetimeFree { ... }
    fn try_specialize_static<T>(self) -> Result<T, Self>
       where Self: 'static + Sized,
             T: 'static { ... }
    fn try_specialize_ref<T>(&self) -> Option<&T>
       where T: ?Sized + LifetimeFree { ... }
    fn try_specialize_from_ref<T>(other: &T) -> Option<&Self>
       where T: ?Sized + LifetimeFree { ... }
    fn try_specialize_ref_static<T>(&self) -> Option<&T>
       where Self: 'static,
             T: ?Sized + 'static { ... }
    fn try_specialize_mut<T>(&mut self) -> Option<&mut T>
       where T: ?Sized + LifetimeFree { ... }
    fn try_specialize_from_mut<T>(other: &mut T) -> Option<&mut Self>
       where T: ?Sized + LifetimeFree { ... }
    fn try_specialize_mut_static<T>(&mut self) -> Option<&mut T>
       where Self: 'static,
             T: ?Sized + 'static { ... }
    unsafe fn try_specialize_ignore_lifetimes<T>(self) -> Result<T, Self>
       where Self: Sized { ... }
    unsafe fn try_specialize_ref_ignore_lifetimes<T>(&self) -> Option<&T>
       where T: ?Sized { ... }
    unsafe fn try_specialize_mut_ignore_lifetimes<T>(
        &mut self,
    ) -> Option<&mut T>
       where T: ?Sized { ... }
}
Expand description

A trait for specializing one type to another at runtime.

This trait uses Specialization helper struct to perform all conversions. You can use Specialization directly if you need to perform more complex specialization cases or to cache the specializable ability.

Library tests ensure that the specializations are performed at compile time and are fully optimized with no runtime cost at opt-level >= 1. Note that the release profile uses opt-level = 3 by default.

Methods cheat sheet:

Bounds\Operationspecialize T1 to T2specialize &T1 to &T2specialize &mut T1 to &mut T2
T1: 'static
+ LifetimeFree
try_specializetry_specialize_reftry_specialize_mut
T2: 'static
+ LifetimeFree
try_specialize_fromtry_specialize_from_reftry_specialize_from_mut
T1: 'static,
T2: 'static,
try_specialize_statictry_specialize_ref_statictry_specialize_mut_static
unconstrained
underlying type
maybe impls
LifetimeFree
..._if_lifetime_free_weak..._ref_if_lifetime_free_weak..._mut_if_lifetime_free_weak
unconstrained
unsafe without
lifetimes check
..._ignore_lifetimes..._ref_ignore_lifetimes..._mut_ignore_lifetimes

Provided Methods§

Source

fn try_specialize<T>(self) -> Result<T, Self>
where Self: Sized, T: LifetimeFree,

Attempts to specialize Self as T for types without lifetimes.

Returns Self as T wrapped in Ok if Self and T types are identical. Otherwise, it returns Self wrapped in Err.

Note that this method requires target type to implement LifetimeFree. Use TrySpecialize::try_specialize_from if your target type doesn’t have LifetimeFree bound but source type does. Use TrySpecialize::try_specialize_static if both source and target type have 'static bounds.

The LifetimeFree trait is not automatically derived for all lifetime-free types. The library only implements it for standard library types that do not have any lifetime parameters.

§Examples

Simple partial reimplementation of stdlib ToString:

use core::fmt::Display;

use try_specialize::TrySpecialize;

fn to_string<T>(value: T) -> String
where
    T: Display,
{
    match value.try_specialize() {
        Ok(string) => string,
        Err(value) => format!("{value}"),
    }
}

assert_eq!(to_string("abc".to_owned()), "abc".to_owned()); // Specialized.
assert_eq!(to_string(123), String::from("123")); // Default.

Note that many standard library types and traits, including ToString, already use specialization for optimization purposes using min_specialization nightly feature.

Source

fn try_specialize_from<T>(other: T) -> Result<Self, T>
where Self: Sized, T: LifetimeFree,

Attempts to specialize T as Self for types without lifetimes.

Returns T as Self wrapped in Ok if Self and T types are identical. Otherwise, it returns T wrapped in Err.

Note that this method requires source type to implement LifetimeFree. Use TrySpecialize::try_specialize_from if your source type doesn’t have LifetimeFree bound but target type does. Use TrySpecialize::try_specialize_static if both target and source type have 'static bounds. The LifetimeFree trait is not automatically derived for all lifetime-free types. The library only implements it for standard library types that do not have any lifetime parameters.

§Examples

Generate a placeholder with non-default value for some types:

use try_specialize::{Specialization, TrySpecialize};

fn placeholder<T>() -> T
where
    T: Default,
{
    None.or_else(|| T::try_specialize_from(12_u8).ok())
        .or_else(|| T::try_specialize_from(234_u16).ok())
        .or_else(|| T::try_specialize_from(3456_u32).ok())
        .or_else(|| T::try_specialize_from(45678_u64).ok())
        .or_else(|| T::try_specialize_from(567_890_u128).ok())
        .or_else(|| T::try_specialize_from(123_456_789_usize).ok())
        .or_else(|| T::try_specialize_from(123.456_f32).ok())
        .or_else(|| T::try_specialize_from(123.456_f64).ok())
        .or_else(|| {
            // SAFETY: For any `'a` It is safe to specialize `&'static str`
            // as `&'a str`.
            unsafe { "dummy string".try_specialize_ignore_lifetimes() }.ok()
        })
        .or_else(|| {
            let spec/*: Specialization<T, String>*/ = Specialization::try_new()?;
            Some(spec.rev().specialize(String::from("foobar")))
        })
        .or_else(|| {
            let spec/*: Specialization<T, Box<str>>*/ = Specialization::try_new()?;
            Some(spec.rev().specialize(String::from("bazz").into_boxed_str()))
        })
        .unwrap_or_default()
}

assert_eq!(placeholder::<(u8, u8)>(), (0, 0));
assert_eq!(placeholder::<u32>(), 3456);
assert_eq!(placeholder::<f64>(), 123.456_f64);
assert_eq!(placeholder::<u128>(), 567_890);
assert_eq!(placeholder::<i128>(), 0);
assert_eq!(placeholder::<&'static str>(), "dummy string");
assert_eq!(placeholder::<String>(), String::from("foobar"));
assert_eq!(placeholder::<Box<str>>(), Box::from("bazz"));
Source

fn try_specialize_static<T>(self) -> Result<T, Self>
where Self: 'static + Sized, T: 'static,

Attempts to specialize Self as T for static types.

Returns Self as T wrapped in Ok if Self and T types are identical. Otherwise, it returns Self wrapped in Err.

Note that this method requires both types to have 'static lifetime, but don’t require any type to implement LifetimeFree. If one of your types does not have a ’static bounds but the other type implements LifetimeFree use TrySpecialize::try_specialize or TrySpecialize::try_specialize_from instead.

§Note

This function requires both the source and destination types to implement 'static. Although most 'static types in Rust can be subtypes to a non-'static alternatives this is not always the case. For example fn(&'static str) and fn(&'a str) have the same TypeId however you can’t subtype the first to the second, because, unlike anything else in the language, functions are contravariant over their arguments. See https://doc.rust-lang.org/reference/subtyping.html#variance and https://doc.rust-lang.org/nomicon/subtyping.html#variance for more details.

§Examples

Function with specialized implementation for std::collections::HashMap and hashbrown::HashMap:

use try_specialize::TrySpecialize;

fn process_static<T>(value: T)
where
    T: 'static,
{
    match value.try_specialize_static::<std::collections::HashMap<u32, char>>() {
        Ok(hash_map @ std::collections::HashMap { .. }) => {
            drop(hash_map);
            // specialized impl for `std::collections::HashMap`
        }
        Err(value) => {
            match value.try_specialize_static::<hashbrown::HashMap<u32, char>>() {
                Ok(hash_map @ hashbrown::HashMap { .. }) => {
                    drop(hash_map);
                    // specialized impl for `hashbrown::HashMap`
                }
                Err(default) => {
                    drop(default);
                    // default impl ...
                }
            }
        }
    }
}
Source

fn try_specialize_ref<T>(&self) -> Option<&T>
where T: ?Sized + LifetimeFree,

Attempts to specialize &Self as &T for types without lifetimes.

Note that this method requires target type to implement LifetimeFree. Use TrySpecialize::try_specialize_from_ref if your target type doesn’t implement LifetimeFree but source type does.

The LifetimeFree trait is not automatically derived for all lifetime-free types. The library only implements it for standard library types that do not have any lifetime parameters.

§Examples

Stringifiable type concatenation, that don’t allocate memory if one of the arguments is empty &str:

use core::fmt::Display;
use std::borrow::Cow;
use std::format;

use try_specialize::TrySpecialize;

fn concat<'a, T1, T2>(first: &'a T1, second: &'a T2) -> Cow<'a, str>
where
    T1: ?Sized + Display,
    T2: ?Sized + Display,
{
    match (
        first.try_specialize_ref(),
        second.try_specialize_ref(),
    ) {
        (Some(first), Some("")) => Cow::Borrowed(first),
        (Some(""), Some(second)) => Cow::Borrowed(second),
        (_, _) => Cow::Owned(format!("{first}{second}")),
    }
}

assert!(matches!(concat("foo", "bar"), Cow::Owned(v) if v == "foobar"));
assert!(matches!(concat("foo", ""), Cow::Borrowed("foo")));
assert!(matches!(concat("", "bar"), Cow::Borrowed("bar")));
let foo = String::from("foo");
let bar = String::from("bar");
assert!(matches!(concat(&foo, &bar), Cow::Owned(v) if v == "foobar"));
assert!(matches!(concat("foo", &456), Cow::Owned(v) if v == "foo456"));
assert!(matches!(concat(&123, &456), Cow::Owned(v) if v == "123456"));
Source

fn try_specialize_from_ref<T>(other: &T) -> Option<&Self>
where T: ?Sized + LifetimeFree,

Attempts to specialize &T as &Self for types without lifetimes.

Note that this method requires source type to implement LifetimeFree. Use TrySpecialize::try_specialize_ref if your source type doesn’t implement LifetimeFree but target type does. The LifetimeFree trait is not automatically derived for all lifetime-free types. The library only implements it for standard library types that do not have any lifetime parameters.

Source

fn try_specialize_ref_static<T>(&self) -> Option<&T>
where Self: 'static, T: ?Sized + 'static,

Attempts to specialize &Self as &T for static types.

Note that this method requires both types to have 'static lifetime, but don’t require any type to implement LifetimeFree.

Source

fn try_specialize_mut<T>(&mut self) -> Option<&mut T>
where T: ?Sized + LifetimeFree,

Attempts to specialize &mut Self as &mut T for types without lifetimes.

Note that this method requires target type to implement LifetimeFree. Use TrySpecialize::try_specialize_from_mut if your target type doesn’t implement LifetimeFree but source type does.

The LifetimeFree trait is not automatically derived for all lifetime-free types. The library only implements it for standard library types that do not have any lifetime parameters.

Source

fn try_specialize_from_mut<T>(other: &mut T) -> Option<&mut Self>
where T: ?Sized + LifetimeFree,

Attempts to specialize &mut T as &mut Self for types without lifetimes.

Note that this method requires source type to implement LifetimeFree. Use TrySpecialize::try_specialize_mut if your source type doesn’t implement LifetimeFree but target type does. The LifetimeFree trait is not automatically derived for all lifetime-free types. The library only implements it for standard library types that do not have any lifetime parameters.

Source

fn try_specialize_mut_static<T>(&mut self) -> Option<&mut T>
where Self: 'static, T: ?Sized + 'static,

Attempts to specialize &mut Self as &mut T for static types.

Note that this method requires both types to have 'static lifetime, but don’t require any type to implement LifetimeFree.

Source

unsafe fn try_specialize_ignore_lifetimes<T>(self) -> Result<T, Self>
where Self: Sized,

Attempts to specialize Self as T ignoring lifetimes.

Returns T as Self wrapped in Ok if Self and T types are identical. Otherwise, it returns T wrapped in Err.

§Safety

This method doesn’t validate type lifetimes. Lifetimes equality should be validated separately.

Calling this method for types with any differences in lifetimes between Self and T types is undefined behavior.

§Examples

Generate a placeholder with non-default value for some types with several &'static T types:

use try_specialize::{Specialization, TrySpecialize};

fn placeholder<T>() -> T
where
    T: Default,
{
    None.or_else(|| T::try_specialize_from(12_u8).ok())
        .or_else(|| T::try_specialize_from(234_u16).ok())
        .or_else(|| T::try_specialize_from(3456_u32).ok())
        .or_else(|| {
            // SAFETY: For any `'a` It is safe to specialize `&'static str`
            // as `&'a str`.
            unsafe { "dummy string".try_specialize_ignore_lifetimes() }.ok()
        })
        .or_else(|| {
            // Ensure that the slice is static.
            const DEFAULT: &'static [u8] = &[1, 2, 3, 4, 5];
            // SAFETY: For any `'a` It is safe to specialize `&'static [u8]`
            // as `&'a [u8]`.
            unsafe { DEFAULT.try_specialize_ignore_lifetimes() }.ok()
        })
        .unwrap_or_default()
}

assert_eq!(placeholder::<(u8, u8)>(), (0, 0));
assert_eq!(placeholder::<u32>(), 3456);
assert_eq!(placeholder::<f64>(), 0.0);
assert_eq!(placeholder::<&'static str>(), "dummy string");
assert_eq!(placeholder::<&'static [u8]>(), &[1, 2, 3, 4, 5]);
Source

unsafe fn try_specialize_ref_ignore_lifetimes<T>(&self) -> Option<&T>
where T: ?Sized,

Attempts to specialize &Self as &T ignoring lifetimes.

§Safety

This method doesn’t validate type lifetimes. Lifetimes equality should be validated separately.

Calling this method for types with any differences in lifetimes between Self and T types is undefined behavior.

Source

unsafe fn try_specialize_mut_ignore_lifetimes<T>(&mut self) -> Option<&mut T>
where T: ?Sized,

Attempts to specialize &mut Self as &mut T ignoring lifetimes.

§Safety

This method doesn’t validate type lifetimes. Lifetimes equality should be validated separately.

Calling this method for types with any differences in lifetimes between Self and T types is undefined behavior.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl<T> TrySpecialize for T
where T: ?Sized,