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\Operation | specialize T1 to T2 | specialize &T1 to &T2 | specialize &mut T1 to &mut T2 |
|---|---|---|---|
T1: 'static + LifetimeFree | try_specialize | try_specialize_ref | try_specialize_mut |
T2: 'static + LifetimeFree | try_specialize_from | try_specialize_from_ref | try_specialize_from_mut |
T1: 'static, T2: 'static, | try_specialize_static | try_specialize_ref_static | try_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§
Sourcefn try_specialize<T>(self) -> Result<T, Self>where
Self: Sized,
T: LifetimeFree,
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.
Sourcefn try_specialize_from<T>(other: T) -> Result<Self, T>where
Self: Sized,
T: LifetimeFree,
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"));Sourcefn try_specialize_static<T>(self) -> Result<T, Self>where
Self: 'static + Sized,
T: 'static,
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 ...
}
}
}
}
}Sourcefn try_specialize_ref<T>(&self) -> Option<&T>where
T: ?Sized + LifetimeFree,
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"));Sourcefn try_specialize_from_ref<T>(other: &T) -> Option<&Self>where
T: ?Sized + LifetimeFree,
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.
Sourcefn try_specialize_ref_static<T>(&self) -> Option<&T>where
Self: 'static,
T: ?Sized + 'static,
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.
Sourcefn try_specialize_mut<T>(&mut self) -> Option<&mut T>where
T: ?Sized + LifetimeFree,
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.
Sourcefn try_specialize_from_mut<T>(other: &mut T) -> Option<&mut Self>where
T: ?Sized + LifetimeFree,
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.
Sourcefn try_specialize_mut_static<T>(&mut self) -> Option<&mut T>where
Self: 'static,
T: ?Sized + 'static,
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.
Sourceunsafe fn try_specialize_ignore_lifetimes<T>(self) -> Result<T, Self>where
Self: Sized,
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]);Sourceunsafe fn try_specialize_ref_ignore_lifetimes<T>(&self) -> Option<&T>where
T: ?Sized,
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.
Sourceunsafe fn try_specialize_mut_ignore_lifetimes<T>(&mut self) -> Option<&mut T>where
T: ?Sized,
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.