Struct TypeId

pub struct TypeId { pub(crate) data: [*const (); 2] }

A TypeId represents a globally unique identifier for a type.

Each TypeId is an opaque object which does not allow inspection of what's inside but does allow basic operations such as cloning, comparison, printing, and showing.

A TypeId is currently only available for types which ascribe to 'static, but this limitation may be removed in the future.

While TypeId implements Hash, PartialOrd, and Ord, it is worth noting that the hashes and ordering will vary between Rust releases. Beware of relying on them inside of your code!

Layout

Like other Rust-representation types, TypeId's size and layout are unstable. In particular, this means that you cannot rely on the size and layout of TypeId remaining the same between Rust releases; they are subject to change without prior notice between Rust releases.

Danger of Improper Variance

You might think that subtyping is impossible between two static types, but this is false; there exists a static type with a static subtype. To wit, fn(&str), which is short for for<'any> fn(&'any str), and fn(&'static str), are two distinct, static types, and yet, fn(&str) is a subtype of fn(&'static str), since any value of type fn(&str) can be used where a value of type fn(&'static str) is needed.

This means that abstractions around TypeId, despite its 'static bound on arguments, still need to worry about unnecessary and improper variance: it is advisable to strive for invariance first. The usability impact will be negligible, while the reduction in the risk of unsoundness will be most welcome.

Examples

Suppose SubType is a subtype of SuperType, that is, a value of type SubType can be used wherever a value of type SuperType is expected. Suppose also that CoVar<T> is a generic type, which is covariant over T (like many other types, including PhantomData<T> and Vec<T>).

Then, by covariance, CoVar<SubType> is a subtype of CoVar<SuperType>, that is, a value of type CoVar<SubType> can be used wherever a value of type CoVar<SuperType> is expected.

Then if CoVar<SuperType> relies on TypeId::of::<SuperType>() to uphold any invariants, those invariants may be broken because a value of type CoVar<SuperType> can be created without going through any of its methods, like so:

type SubType = fn(&());
type SuperType = fn(&'static ());
type CoVar<T> = Vec<T>; // imagine something more complicated

let sub: CoVar<SubType> = CoVar::new();
// we have a `CoVar<SuperType>` instance without
// *ever* having called `CoVar::<SuperType>::new()`!
let fake_super: CoVar<SuperType> = sub;

The following is an example program that tries to use TypeId::of to implement a generic type Unique<T> that guarantees unique instances for each Unique<T>, that is, for each type T there can be at most one value of type Unique<T> at any time.

mod unique {
    use std::any::TypeId;
    use std::collections::BTreeSet;
    use std::marker::PhantomData;
    use std::sync::Mutex;

    static ID_SET: Mutex<BTreeSet<TypeId>> = Mutex::new(BTreeSet::new());

    // TypeId has only covariant uses, which makes Unique covariant over TypeAsId 🚨
    #[derive(Debug, PartialEq)]
    pub struct Unique<TypeAsId: 'static>(
        // private field prevents creation without `new` outside this module
        PhantomData<TypeAsId>,
    );

    impl<TypeAsId: 'static> Unique<TypeAsId> {
        pub fn new() -> Option<Self> {
            let mut set = ID_SET.lock().unwrap();
            (set.insert(TypeId::of::<TypeAsId>())).then(|| Self(PhantomData))
        }
    }

    impl<TypeAsId: 'static> Drop for Unique<TypeAsId> {
        fn drop(&mut self) {
            let mut set = ID_SET.lock().unwrap();
            (!set.remove(&TypeId::of::<TypeAsId>())).then(|| panic!("duplicity detected"));
        }
    }
}

use unique::Unique;

// `OtherRing` is a subtype of `TheOneRing`. Both are 'static, and thus have a TypeId.
type TheOneRing = fn(&'static ());
type OtherRing = fn(&());

fn main() {
    let the_one_ring: Unique<TheOneRing> = Unique::new().unwrap();
    assert_eq!(Unique::<TheOneRing>::new(), None);

    let other_ring: Unique<OtherRing> = Unique::new().unwrap();
    // Use that `Unique<OtherRing>` is a subtype of `Unique<TheOneRing>` 🚨
    let fake_one_ring: Unique<TheOneRing> = other_ring;
    assert_eq!(fake_one_ring, the_one_ring);

    std::mem::forget(fake_one_ring);
}

Fields

data: [*const (); 2]

This needs to be an array of pointers, since there is provenance in the first array field. This provenance knows exactly which type the TypeId actually is, allowing CTFE and miri to operate based off it. At runtime all the pointers in the array contain bits of the hash, making the entire TypeId actually just be a u128 hash of the type.

Implementations

impl TypeId

const fn is_signed(self) -> bool

Returns true if the type represented by this TypeId is an signed integer.

For everything else this returns false.

Examples

#![feature(type_info)]
use std::any::TypeId;

assert_eq!(const { TypeId::of::<i32>().is_signed() }, true);
assert_eq!(const { TypeId::of::<u8>().is_signed() }, false);
assert_eq!(const { TypeId::of::<bool>().is_signed() }, false);
const fn element_ty(self) -> Option<TypeId>

When called on a TypeId representing an array or slice this returns the type of each element otherwise this returns None.

Examples

#![feature(type_info)]
use std::any::TypeId;

assert_eq!(const { TypeId::of::<[u32; 16]>().element_ty() }, Some(TypeId::of::<u32>()));
assert_eq!(const { TypeId::of::<u8>().element_ty() }, None); // not an array or slice
const fn array_len(self) -> usize

When called on a TypeId representing an array this returns the length of the array in all other cases this returns zero.

Examples

#![feature(type_info)]
use std::any::TypeId;

assert_eq!(const { TypeId::of::<[u32; 16]>().array_len() }, 16);
assert_eq!(const { TypeId::of::<u8>().array_len() }, 0); // not an array
const fn size(self) -> Option<usize>

Returns the size of the type represented by this TypeId. None if it is unsized.

Examples

#![feature(type_info)]
use std::any::TypeId;

assert_eq!(const { TypeId::of::<u32>().size() }, Some(4));
assert_eq!(const { TypeId::of::<[u8; 16]>().size() }, Some(16));
const fn variants(self) -> usize

Returns the number of variants of the type represented by this TypeId.

For enums, this is the number of variants. For structs and unions, this is always 1.

#![feature(type_info)]
use std::any::TypeId;

assert_eq!(const { TypeId::of::<Option<()>>().variants() }, 2);

struct Unit;
struct Point {
    x: u32,
    y: u32,
}
assert_eq!(const { TypeId::of::<Unit>().variants() }, 1);
assert_eq!(const { TypeId::of::<Point>().variants() }, 1);
assert_eq!(const { TypeId::of::<(f32, f32)>().variants() }, 1);
const fn variant(self, variant_index: usize) -> VariantId

Returns the variant representing type at the given index of the type represented by this TypeId. Use it to get the name of an enum variant or check whether it is non_exhaustive.

#![feature(type_info)]
use std::any::TypeId;

enum Enum {
    Unit,
    Tuple(u32, u64),
    #[non_exhaustive]
    Struct { x: u32, y: u32, z: String },
}
assert_eq!(const { TypeId::of::<Enum>().variant(1).name() }, "Tuple");
assert_eq!(const { TypeId::of::<Enum>().variant(2).name() }, "Struct");

assert_eq!(const { TypeId::of::<Enum>().variant(1).non_exhaustive() }, false);
assert_eq!(const { TypeId::of::<Enum>().variant(2).non_exhaustive() }, true);

The variant index refer to the source order index of a variant in a type.

Variant indexes are always 0..variant_count, regardless of any custom discriminants that may have been defined.

enum Enum {
    Foo,  // variant index == 0
    Bar,  // variant index == 1
}

Calling variant on the TypeId for a struct will be treated as a compile-time error. The same is true for out-of-bounds indexing on an enum.

# #![feature(type_info)]
# use std::any::TypeId;
#
# struct Point {
#     x: u32,
#     y: u32,
# }
# enum Enum {
#     Unit,
#     Tuple(u32, u64),
#     Struct { x: u32, y: u32, z: String },
# }
const {
    _ = TypeId::of::<Point>().variant(0); // error: cannot get the variant of a struct
    _ = TypeId::of::<Enum>().variant(10); // error: indexing out of bounds: the len is 3 but the index is 10
}
const fn fields(self, variant_index: usize) -> usize

Returns the number of fields at the given variant_index of the type represented by this TypeId.

#![feature(type_info)]
use std::any::TypeId;

assert_eq!(const { TypeId::of::<u32>().fields(0) }, 0);

struct Point {
    x: u32,
    y: u32,
}
assert_eq!(const { TypeId::of::<Point>().fields(0) }, 2);

enum Enum {
    Unit,
    Tuple(u32, u64),
    Struct { x: u32, y: u32, z: String },
}
assert_eq!(const { TypeId::of::<Enum>().fields(0) }, 0);
assert_eq!(const { TypeId::of::<Enum>().fields(1) }, 2);
assert_eq!(const { TypeId::of::<Enum>().fields(2) }, 3);

The variant index refers to the source order index of a variant in a type.

For enums, these are always 0..variant_count, regardless of any custom discriminants that may have been defined. structs, tuples, and unionss are considered to have a single variant with variant index zero.

enum Number {
    Seven = 7, // variant index == 0
    Six = 6,   // variant index == 1
}

Out-of-bounds indexing will be treated as a compile-time error.

# #![feature(type_info)]
# use std::any::TypeId;
#
# struct Point {
#     x: u32,
#     y: u32,
# }
# enum Enum {
#     Unit,
#     Tuple(u32, u64),
#     Struct { x: u32, y: u32, z: String },
# }
const {
    _ = TypeId::of::<Point>().fields(10); // error: indexing out of bounds: the len is 2 but the index is 10
    _ = TypeId::of::<Enum>().fields(10); // error: indexing out of bounds: the len is 3 but the index is 10
}
const fn field(self, variant_index: usize, field_index: usize) -> FieldId

Returns the field representing type at the given index of the type represented by this TypeId.

#![feature(type_info)]
use std::any::TypeId;

struct Point {
    x: u32,
    y: u32,
}
assert_eq!(const { TypeId::of::<Point>().field(0, 0).type_id() }, TypeId::of::<u32>());
assert_eq!(const { TypeId::of::<Point>().field(0, 1).type_id() }, TypeId::of::<u32>());

enum Enum {
    Unit,
    Tuple(u32, u64),
    Struct { x: u32, y: u32, z: String },
}
assert_eq!(const { TypeId::of::<Enum>().field(1, 0).type_id() }, TypeId::of::<u32>());
assert_eq!(const { TypeId::of::<Enum>().field(2, 2).type_id() }, TypeId::of::<String>());

The variant index and field index refer to the source order index of a variant in a type and the source order index of a field in a variant, respectively.

For enums, variant indexes are always 0..variant_count, regardless of any custom discriminants that may have been defined. structs, tuples, and unionss are considered to have a single variant with variant index zero.

As for field indexes, they may not be the same as the layout order for repr(Rust) types, but they are for repr(C) types.

enum Enum {
    Foo,  // variant index == 0
    Bar { // variant index == 1
        a: (), // field index == 0 in `Bar`
        b: (), // field index == 1 in `Bar`
    }
}

Out-of-bounds indexing will be treated as a compile-time error.

# #![feature(type_info)]
# use std::any::TypeId;
#
# struct Point {
#     x: u32,
#     y: u32,
# }
# enum Enum {
#     Unit,
#     Tuple(u32, u64),
#     Struct { x: u32, y: u32, z: String },
# }
const {
    _ = TypeId::of::<Point>().field(0, 10); // error: indexing out of bounds: the len is 2 but the index is 10
    _ = TypeId::of::<Enum>().field(2, 10); // error: indexing out of bounds: the len is 3 but the index is 10
}
const fn non_exhaustive(self) -> bool

Returns whether a type is marked with #[non_exhaustive]. Returns false for everything but adts.

const fn generics(self) -> &'static [Generic]

Returns a list of generic parameters of the type. Returns an empty slice for everything that doesn't have generics.

const fn points_to(self) -> Option<TypeId>

Given a TypeId that represents a pointer this returns the TypeId which that pointer points to. When called on anything else this returns None.

#![feature(type_info)]
use std::any::TypeId;

assert_eq!(
    const { TypeId::of::<&i32>().points_to() },
    const { Some(TypeId::of::<i32>()) },
);

assert_eq!(
    const { TypeId::of::<*const i32>().points_to() },
    const { Some(TypeId::of::<i32>()) },
);
const fn points_mutably(self) -> bool

Given a TypeId that represents a pointer returns whether that pointer is mutable. When called on anything else this returns false.

#![feature(type_info)]
use std::any::TypeId;

assert!(const { TypeId::of::<&mut i32>().points_mutably() });
assert!(const { !TypeId::of::<&i32>().points_mutably() });

assert!(!const { TypeId::of::<*const i32>().points_mutably() });
assert!(const { TypeId::of::<*mut i32>().points_mutably() });
const fn function_ptr(self) -> Option<FnPtr>

Given a TypeId that represents a function pointer returns an FnPtr. When called on something else this returns None.

#![feature(type_info)]
use std::any::TypeId;
use std::mem::type_info::{Abi, FnPtr};

const F: FnPtr = TypeId::of::<fn(u8, u16) -> usize>()
    .function_ptr()
    .expect("TypeId of a function ptr");

assert!(F.inputs() == [TypeId::of::<u8>(), TypeId::of::<u16>()]);
assert!(F.output() == TypeId::of::<usize>());
assert!(F.abi() == Abi::default());
#![feature(type_info)]
# use std::any::TypeId;
# use std::mem::type_info::{Abi, FnPtr};
#
const F: FnPtr = TypeId::of::<unsafe fn()>()
    .function_ptr()
    .expect("TypeId of a function ptr");

assert!(F.inputs() == []);
assert!(F.output() == TypeId::of::<()>());
assert!(F.abi() == Abi::default());

impl TypeId

const fn of<T: ?Sized + 'static>() -> TypeId

Returns the TypeId of the generic type parameter.

Examples

use std::any::{Any, TypeId};

fn is_string<T: ?Sized + Any>(_s: &T) -> bool {
    TypeId::of::<String>() == TypeId::of::<T>()
}

assert_eq!(is_string(&0), false);
assert_eq!(is_string(&"cookie monster".to_string()), true);
const fn trait_info_of<'a, T: TryAsDynCompatible<'a> + ?Sized>(self) -> Option<TraitImpl<T>>

Checks if the [TypeId] implements the trait. If it does it returns [TraitImpl] which can be used to build a fat pointer. It can only be called at compile time. self must be the [TypeId] of a sized type or None will be returned.

Examples

#![feature(type_info)]
use std::any::{TypeId};

pub trait Blah {}
impl Blah for u8 {}

assert!(const { TypeId::of::<u8>().trait_info_of::<dyn Blah>() }.is_some());
assert!(const { TypeId::of::<u16>().trait_info_of::<dyn Blah>() }.is_none());
const fn trait_info_of_trait_type_id(self, trait_represented_by_type_id: TypeId) -> Option<TraitImpl<*const ()>>

Checks if the [TypeId] implements the trait of trait_represented_by_type_id. If it does it returns [TraitImpl] which can be used to build a fat pointer. It can only be called at compile time. self must be the [TypeId] of a sized type or None will be returned.

Examples

#![feature(type_info)]
use std::any::{TypeId};

pub trait Blah {}
impl Blah for u8 {}

assert!(const { TypeId::of::<u8>().trait_info_of_trait_type_id(TypeId::of::<dyn Blah>()) }.is_some());
assert!(const { TypeId::of::<u16>().trait_info_of_trait_type_id(TypeId::of::<dyn Blah>()) }.is_none());
fn as_u128(self) -> u128

impl TypeId

const fn info(self) -> Type

Compute the type information of a concrete type. It can only be called at compile time.

Trait Implementations

impl Clone for TypeId

fn clone(&self) -> Self

impl Copy for TypeId

impl Debug for TypeId

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

impl Eq for TypeId

fn assert_fields_are_eq(&self)

impl Hash for TypeId

fn hash<H: Hasher>(&self, state: &mut H)

impl Ord for TypeId

fn cmp(&self, other: &Self) -> Ordering

impl PartialEq for TypeId

fn eq(&self, other: &Self) -> bool

impl PartialOrd for TypeId

fn partial_cmp(&self, other: &Self) -> Option<Ordering>

impl Send for TypeId

impl Sync for TypeId

impl TrivialClone for TypeId

Auto Trait Implementations

impl Freeze for TypeId

impl RefUnwindSafe for TypeId

impl Unpin for TypeId

impl UnsafeUnpin for TypeId

impl UnwindSafe for TypeId

Blanket Implementations

impl<T> Any for TypeId where T: 'static + ?Sized,

fn type_id(&self) -> TypeId

impl<T> Borrow<T> for TypeId where T: ?Sized,

fn borrow(&self) -> &T

impl<T> BorrowMut<T> for TypeId where T: ?Sized,

fn borrow_mut(&mut self) -> &mut T

impl<T> CloneToUninit for TypeId where T: Clone,

unsafe fn clone_to_uninit(&self, dest: *mut u8)

impl<T> From<T> for TypeId

fn from(t: T) -> T

Returns the argument unchanged.

impl<T> Printable for TypeId where T: Copy + Debug,

impl<T> SizeHint for TypeId where T: ?Sized,

fn lower_bound(&self) -> usize
fn upper_bound(&self) -> Option<usize>

impl<T> SizedTypeProperties for TypeId

impl<T, U> Into<U> for TypeId where U: From<T>,

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

impl<T, U> TryFrom<U> for TypeId where U: Into<T>,

type Error = never;
fn try_from(value: U) -> Result<T, never>

impl<T, U> TryInto<U> for TypeId where U: TryFrom<T>,

type Error = <U as TryFrom<T>>::Error;
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>