erased_discriminant/
lib.rs1#![no_std]
33#![doc(html_root_url = "https://docs.rs/erased-discriminant/1.0.1")]
34#![deny(unsafe_op_in_unsafe_fn)]
35#![allow(clippy::doc_markdown, clippy::missing_safety_doc)]
36
37extern crate alloc;
38
39use alloc::boxed::Box;
40use core::any::TypeId;
41use core::fmt::{self, Debug};
42use core::hash::{Hash, Hasher};
43use core::mem::{self, MaybeUninit};
44
45pub struct Discriminant {
47 data: MaybeUninit<*mut ()>,
48 vtable: &'static DiscriminantVTable,
49}
50
51impl Discriminant {
52 pub fn of<T>(value: &T) -> Self {
53 let discriminant = mem::discriminant(value);
54 let data = if small_discriminant::<T>() {
55 let mut data = MaybeUninit::<*mut ()>::uninit();
56 unsafe {
57 data.as_mut_ptr()
58 .cast::<core::mem::Discriminant<T>>()
59 .write(discriminant);
60 }
61 data
62 } else {
63 MaybeUninit::new(Box::into_raw(Box::new(discriminant)).cast::<()>())
64 };
65 Discriminant {
66 data,
67 vtable: &DiscriminantVTable {
68 eq: discriminant_eq::<T>,
69 hash: discriminant_hash::<T>,
70 clone: discriminant_clone::<T>,
71 debug: discriminant_debug::<T>,
72 drop: discriminant_drop::<T>,
73 type_id: typeid::of::<core::mem::Discriminant<T>>,
74 },
75 }
76 }
77}
78
79fn small_discriminant<T>() -> bool {
80 mem::size_of::<core::mem::Discriminant<T>>() <= mem::size_of::<*const ()>()
81}
82
83struct DiscriminantVTable {
84 eq: unsafe fn(this: &Discriminant, other: &Discriminant) -> bool,
85 hash: unsafe fn(this: &Discriminant, hasher: &mut dyn Hasher),
86 clone: unsafe fn(this: &Discriminant) -> Discriminant,
87 debug: unsafe fn(this: &Discriminant, formatter: &mut fmt::Formatter) -> fmt::Result,
88 drop: unsafe fn(this: &mut Discriminant),
89 type_id: fn() -> TypeId,
90}
91
92unsafe fn as_ref<T>(this: &Discriminant) -> &core::mem::Discriminant<T> {
93 unsafe {
94 &*if small_discriminant::<T>() {
95 this.data.as_ptr().cast::<core::mem::Discriminant<T>>()
96 } else {
97 this.data.assume_init().cast::<core::mem::Discriminant<T>>()
98 }
99 }
100}
101
102unsafe fn discriminant_eq<T>(this: &Discriminant, other: &Discriminant) -> bool {
103 (other.vtable.type_id)() == typeid::of::<core::mem::Discriminant<T>>()
104 && unsafe { as_ref::<T>(this) } == unsafe { as_ref::<T>(other) }
105}
106
107unsafe fn discriminant_hash<T>(this: &Discriminant, mut hasher: &mut dyn Hasher) {
108 typeid::of::<core::mem::Discriminant<T>>().hash(&mut hasher);
109 unsafe { as_ref::<T>(this) }.hash(&mut hasher);
110}
111
112unsafe fn discriminant_clone<T>(this: &Discriminant) -> Discriminant {
113 if small_discriminant::<T>() {
114 Discriminant {
115 data: this.data,
116 vtable: this.vtable,
117 }
118 } else {
119 let discriminant = unsafe { *this.data.assume_init().cast::<core::mem::Discriminant<T>>() };
120 Discriminant {
121 data: MaybeUninit::new(Box::into_raw(Box::new(discriminant)).cast::<()>()),
122 vtable: this.vtable,
123 }
124 }
125}
126
127unsafe fn discriminant_debug<T>(
128 this: &Discriminant,
129 formatter: &mut fmt::Formatter,
130) -> fmt::Result {
131 Debug::fmt(unsafe { as_ref::<T>(this) }, formatter)
132}
133
134unsafe fn discriminant_drop<T>(this: &mut Discriminant) {
135 if !small_discriminant::<T>() {
136 let _ =
137 unsafe { Box::from_raw(this.data.assume_init().cast::<core::mem::Discriminant<T>>()) };
138 }
139}
140
141impl Eq for Discriminant {}
142
143impl PartialEq for Discriminant {
144 fn eq(&self, other: &Self) -> bool {
145 unsafe { (self.vtable.eq)(self, other) }
146 }
147}
148
149impl Hash for Discriminant {
150 fn hash<H: Hasher>(&self, hasher: &mut H) {
151 unsafe { (self.vtable.hash)(self, hasher) };
152 }
153}
154
155impl Clone for Discriminant {
156 fn clone(&self) -> Self {
157 unsafe { (self.vtable.clone)(self) }
158 }
159}
160
161impl Debug for Discriminant {
162 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
163 unsafe { (self.vtable.debug)(self, formatter) }
164 }
165}
166
167impl Drop for Discriminant {
168 fn drop(&mut self) {
169 unsafe { (self.vtable.drop)(self) };
170 }
171}