typeid/lib.rs
1//! [![github]](https://github.com/dtolnay/typeid) [![crates-io]](https://crates.io/crates/typeid) [![docs-rs]](https://docs.rs/typeid)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! # Const `TypeId` and non-'static `TypeId`
8//!
9//! <br>
10//!
11//! #### Const `TypeId`
12//!
13//! This crate provides [`ConstTypeId`], which is like [`core::any::TypeId`] but
14//! is constructible in const in stable Rust. (The standard library's TypeId's
15//! is nightly-only to construct in const; the tracking issue for this is
16//! [rust#77125].)
17//!
18//! [rust#77125]: https://github.com/rust-lang/rust/issues/77125
19//!
20//! Being able to construct `ConstTypeId` in const makes it suitable for use
21//! cases that rely on static promotion:
22//!
23//! ```
24//! use std::fmt::{self, Debug, Display};
25//! use std::ptr;
26//! use typeid::ConstTypeId;
27//!
28//! pub struct ObjectVTable {
29//! type_id: ConstTypeId,
30//! drop_in_place: unsafe fn(*mut ()),
31//! display: unsafe fn(*const (), &mut fmt::Formatter) -> fmt::Result,
32//! debug: unsafe fn(*const (), &mut fmt::Formatter) -> fmt::Result,
33//! }
34//!
35//! impl ObjectVTable {
36//! pub const fn new<T: Display + Debug>() -> &'static Self {
37//! &ObjectVTable {
38//! type_id: const { ConstTypeId::of::<T>() },
39//! drop_in_place: |ptr| unsafe { ptr::drop_in_place(ptr.cast::<T>()) },
40//! display: |ptr, f| unsafe { Display::fmt(&*ptr.cast::<T>(), f) },
41//! debug: |ptr, f| unsafe { Debug::fmt(&*ptr.cast::<T>(), f) },
42//! }
43//! }
44//! }
45//! ```
46//!
47//! and in associated constants:
48//!
49//! ```
50//! use typeid::ConstTypeId;
51//!
52//! pub trait GetTypeId {
53//! const TYPEID: ConstTypeId;
54//! }
55//!
56//! impl<T: 'static> GetTypeId for T {
57//! const TYPEID: ConstTypeId = ConstTypeId::of::<Self>();
58//! }
59//! ```
60//!
61//! <br>
62//!
63//! #### Non-'static `TypeId`
64//!
65//! This crate provides [`typeid::of`], which takes an arbitrary non-'static
66//! type `T` and produces the `TypeId` for the type obtained by replacing all
67//! lifetimes in `T` by `'static`, other than higher-rank lifetimes found in
68//! trait objects.
69//!
70//! For example if `T` is `&'b dyn for<'a> Trait<'a, 'c>`, then
71//! `typeid::of::<T>()` produces the TypeId of `&'static dyn for<'a> Trait<'a,
72//! 'static>`.
73//!
74//! It should be obvious that unlike with the standard library's TypeId,
75//! `typeid::of::<A>() == typeid::of::<B>()` does **not** mean that `A` and `B`
76//! are the same type. However, there is a common special case where this
77//! behavior is exactly what is needed. If:
78//!
79//! - `A` is an arbitrary non-'static type parameter, _and_
80//! - `B` is 'static, _and_
81//! - all types with the same id as `B` are also 'static
82//!
83//! then `typeid::of::<A>() == typeid::of::<B>()` guarantees that `A` and `B`
84//! are the same type.
85//!
86//! ```
87//! use core::any::TypeId;
88//! use core::slice;
89//!
90//! pub fn example<T>(slice: &[T]) {
91//! // T is arbitrary and non-'static.
92//!
93//! if typeid::of::<T>() == TypeId::of::<u8>() {
94//! // T is definitely u8
95//! let bytes = unsafe { slice::from_raw_parts(slice.as_ptr().cast(), slice.len()) };
96//! process_bytes(bytes);
97//! } else {
98//! for t in slice {
99//! process(t);
100//! }
101//! }
102//! }
103//!
104//! fn process<T>(_: &T) {/* ... */}
105//! fn process_bytes(_: &[u8]) {/* ... */}
106//! ```
107
108#![no_std]
109#![doc(html_root_url = "https://docs.rs/typeid/1.0.2")]
110#![allow(clippy::doc_markdown, clippy::inline_always)]
111
112extern crate self as typeid;
113
114use core::any::TypeId;
115#[cfg(not(no_const_type_id))]
116use core::cmp::Ordering;
117#[cfg(not(no_const_type_id))]
118use core::fmt::{self, Debug};
119#[cfg(not(no_const_type_id))]
120use core::hash::{Hash, Hasher};
121use core::marker::PhantomData;
122use core::mem;
123
124#[cfg(not(no_const_type_id))]
125#[derive(Copy, Clone)]
126pub struct ConstTypeId {
127 type_id_fn: fn() -> TypeId,
128}
129
130#[cfg(not(no_const_type_id))]
131impl ConstTypeId {
132 #[must_use]
133 pub const fn of<T>() -> Self
134 where
135 T: ?Sized,
136 {
137 ConstTypeId {
138 type_id_fn: typeid::of::<T>,
139 }
140 }
141
142 #[inline]
143 fn get(self) -> TypeId {
144 (self.type_id_fn)()
145 }
146}
147
148#[cfg(not(no_const_type_id))]
149impl Debug for ConstTypeId {
150 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
151 Debug::fmt(&self.get(), formatter)
152 }
153}
154
155#[cfg(not(no_const_type_id))]
156impl PartialEq for ConstTypeId {
157 #[inline]
158 fn eq(&self, other: &Self) -> bool {
159 self.get() == other.get()
160 }
161}
162
163#[cfg(not(no_const_type_id))]
164impl PartialEq<TypeId> for ConstTypeId {
165 fn eq(&self, other: &TypeId) -> bool {
166 self.get() == *other
167 }
168}
169
170#[cfg(not(no_const_type_id))]
171impl Eq for ConstTypeId {}
172
173#[cfg(not(no_const_type_id))]
174impl PartialOrd for ConstTypeId {
175 #[inline]
176 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
177 Some(Ord::cmp(self, other))
178 }
179}
180
181#[cfg(not(no_const_type_id))]
182impl Ord for ConstTypeId {
183 #[inline]
184 fn cmp(&self, other: &Self) -> Ordering {
185 Ord::cmp(&self.get(), &other.get())
186 }
187}
188
189#[cfg(not(no_const_type_id))]
190impl Hash for ConstTypeId {
191 fn hash<H: Hasher>(&self, state: &mut H) {
192 self.get().hash(state);
193 }
194}
195
196#[must_use]
197#[inline(always)]
198pub fn of<T>() -> TypeId
199where
200 T: ?Sized,
201{
202 trait NonStaticAny {
203 fn get_type_id(&self) -> TypeId
204 where
205 Self: 'static;
206 }
207
208 impl<T: ?Sized> NonStaticAny for PhantomData<T> {
209 #[inline(always)]
210 fn get_type_id(&self) -> TypeId
211 where
212 Self: 'static,
213 {
214 TypeId::of::<T>()
215 }
216 }
217
218 let phantom_data = PhantomData::<T>;
219 NonStaticAny::get_type_id(unsafe {
220 mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data)
221 })
222}