/rust/registry/src/index.crates.io-1949cf8c6b5b557f/unty-0.0.4/src/lib.rs
Line | Count | Source |
1 | | #![no_std] |
2 | | //! A crate that allows you to untype your types. |
3 | | //! |
4 | | //! This provides 2 functions: |
5 | | //! |
6 | | //! [`type_equal`] allows you to check if two types are the same. |
7 | | //! |
8 | | //! [`unty`] allows you to downcast a generic type into a concrete type. |
9 | | //! |
10 | | //! This is mostly useful for generic functions, e.g. |
11 | | //! |
12 | | //! ``` |
13 | | //! # use unty::*; |
14 | | //! pub fn foo<S>(s: S) { |
15 | | //! if let Ok(a) = unsafe { unty::<S, u8>(s) } { |
16 | | //! println!("It is an u8 with value {a}"); |
17 | | //! } else { |
18 | | //! println!("it is not an u8"); |
19 | | //! } |
20 | | //! } |
21 | | //! foo(10u8); // will print "it is an u8" |
22 | | //! foo("test"); // will print "it is not an u8" |
23 | | //! ``` |
24 | | //! |
25 | | //! Note that both of these functions may give false positives if both types have lifetimes. There currently is not a way to prevent this. See [`type_equal`] for more information. |
26 | | //! |
27 | | //! ```no_run |
28 | | //! # fn foo<'a>(input: &'a str) { |
29 | | //! # use unty::*; |
30 | | //! assert!(type_equal::<&'a str, &'static str>()); // these are not actually the same |
31 | | //! if let Ok(str) = unsafe { unty::<&'a str, &'static str>(input) } { |
32 | | //! // this will extend the &'a str lifetime to be &'static, which is not allowed. |
33 | | //! // the compiler may now light your PC on fire. |
34 | | //! } |
35 | | //! # } |
36 | | //! ``` |
37 | | |
38 | | use core::{any::TypeId, marker::PhantomData, mem}; |
39 | | |
40 | | /// Untypes your types. For documentation see the root of this crate. |
41 | | /// |
42 | | /// # Safety |
43 | | /// |
44 | | /// This should not be used with types with lifetimes. |
45 | | pub unsafe fn unty<Src, Target: 'static>(x: Src) -> Result<Target, Src> { |
46 | | if type_equal::<Src, Target>() { |
47 | | let x = mem::ManuallyDrop::new(x); |
48 | | Ok(mem::transmute_copy::<Src, Target>(&x)) |
49 | | } else { |
50 | | Err(x) |
51 | | } |
52 | | } |
53 | | |
54 | | /// Checks to see if the two types are equal. |
55 | | /// |
56 | | /// ``` |
57 | | /// # use unty::type_equal; |
58 | | /// assert!(type_equal::<u8, u8>()); |
59 | | /// assert!(!type_equal::<u8, u16>()); |
60 | | /// |
61 | | /// fn is_string<T>(_t: T) -> bool { |
62 | | /// type_equal::<T, String>() |
63 | | /// } |
64 | | /// |
65 | | /// assert!(is_string(String::new())); |
66 | | /// assert!(!is_string("")); // `&'static str`, not `String` |
67 | | /// ``` |
68 | | /// |
69 | | /// Note that this may give false positives if both of the types have lifetimes. Currently it is not possible to determine the difference between `&'a str` and `&'b str`. |
70 | | /// |
71 | | /// ``` |
72 | | /// # use unty::type_equal; |
73 | | /// # fn foo<'a, 'b>(a: &'a str, b: &'b str) { |
74 | | /// assert!(type_equal::<&'a str, &'b str>()); // actual different types, this is not safe to transmute |
75 | | /// # } |
76 | | /// # foo("", ""); |
77 | | /// ``` |
78 | 28.8k | pub fn type_equal<Src: ?Sized, Target: ?Sized>() -> bool { |
79 | 28.8k | non_static_type_id::<Src>() == non_static_type_id::<Target>() |
80 | 28.8k | } unty::type_equal::<compat::AllTypes, u8> Line | Count | Source | 78 | 1.30k | pub fn type_equal<Src: ?Sized, Target: ?Sized>() -> bool { | 79 | 1.30k | non_static_type_id::<Src>() == non_static_type_id::<Target>() | 80 | 1.30k | } |
unty::type_equal::<u8, u8> Line | Count | Source | 78 | 1.71k | pub fn type_equal<Src: ?Sized, Target: ?Sized>() -> bool { | 79 | 1.71k | non_static_type_id::<Src>() == non_static_type_id::<Target>() | 80 | 1.71k | } |
unty::type_equal::<roundtrip::AllTypes, u8> Line | Count | Source | 78 | 22.1k | pub fn type_equal<Src: ?Sized, Target: ?Sized>() -> bool { | 79 | 22.1k | non_static_type_id::<Src>() == non_static_type_id::<Target>() | 80 | 22.1k | } |
unty::type_equal::<u8, u8> Line | Count | Source | 78 | 3.75k | pub fn type_equal<Src: ?Sized, Target: ?Sized>() -> bool { | 79 | 3.75k | non_static_type_id::<Src>() == non_static_type_id::<Target>() | 80 | 3.75k | } |
|
81 | | |
82 | | // Code by dtolnay in a bincode issue: |
83 | | // https://github.com/bincode-org/bincode/issues/665#issue-1903241159 |
84 | 57.7k | fn non_static_type_id<T: ?Sized>() -> TypeId { |
85 | | trait NonStaticAny { |
86 | | fn get_type_id(&self) -> TypeId |
87 | | where |
88 | | Self: 'static; |
89 | | } |
90 | | |
91 | | impl<T: ?Sized> NonStaticAny for PhantomData<T> { |
92 | 57.7k | fn get_type_id(&self) -> TypeId |
93 | 57.7k | where |
94 | 57.7k | Self: 'static, |
95 | 57.7k | { |
96 | 57.7k | TypeId::of::<T>() |
97 | 57.7k | } <core::marker::PhantomData<compat::AllTypes> as unty::non_static_type_id::NonStaticAny>::get_type_id Line | Count | Source | 92 | 1.30k | fn get_type_id(&self) -> TypeId | 93 | 1.30k | where | 94 | 1.30k | Self: 'static, | 95 | 1.30k | { | 96 | 1.30k | TypeId::of::<T>() | 97 | 1.30k | } |
<core::marker::PhantomData<u8> as unty::non_static_type_id::NonStaticAny>::get_type_id Line | Count | Source | 92 | 4.74k | fn get_type_id(&self) -> TypeId | 93 | 4.74k | where | 94 | 4.74k | Self: 'static, | 95 | 4.74k | { | 96 | 4.74k | TypeId::of::<T>() | 97 | 4.74k | } |
<core::marker::PhantomData<roundtrip::AllTypes> as unty::non_static_type_id::NonStaticAny>::get_type_id Line | Count | Source | 92 | 22.1k | fn get_type_id(&self) -> TypeId | 93 | 22.1k | where | 94 | 22.1k | Self: 'static, | 95 | 22.1k | { | 96 | 22.1k | TypeId::of::<T>() | 97 | 22.1k | } |
<core::marker::PhantomData<u8> as unty::non_static_type_id::NonStaticAny>::get_type_id Line | Count | Source | 92 | 29.6k | fn get_type_id(&self) -> TypeId | 93 | 29.6k | where | 94 | 29.6k | Self: 'static, | 95 | 29.6k | { | 96 | 29.6k | TypeId::of::<T>() | 97 | 29.6k | } |
|
98 | | } |
99 | | |
100 | 57.7k | let phantom_data = PhantomData::<T>; |
101 | 57.7k | NonStaticAny::get_type_id(unsafe { |
102 | 57.7k | mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data) |
103 | 57.7k | }) |
104 | 57.7k | } unty::non_static_type_id::<compat::AllTypes> Line | Count | Source | 84 | 1.30k | fn non_static_type_id<T: ?Sized>() -> TypeId { | 85 | | trait NonStaticAny { | 86 | | fn get_type_id(&self) -> TypeId | 87 | | where | 88 | | Self: 'static; | 89 | | } | 90 | | | 91 | | impl<T: ?Sized> NonStaticAny for PhantomData<T> { | 92 | | fn get_type_id(&self) -> TypeId | 93 | | where | 94 | | Self: 'static, | 95 | | { | 96 | | TypeId::of::<T>() | 97 | | } | 98 | | } | 99 | | | 100 | 1.30k | let phantom_data = PhantomData::<T>; | 101 | 1.30k | NonStaticAny::get_type_id(unsafe { | 102 | 1.30k | mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data) | 103 | 1.30k | }) | 104 | 1.30k | } |
unty::non_static_type_id::<u8> Line | Count | Source | 84 | 4.74k | fn non_static_type_id<T: ?Sized>() -> TypeId { | 85 | | trait NonStaticAny { | 86 | | fn get_type_id(&self) -> TypeId | 87 | | where | 88 | | Self: 'static; | 89 | | } | 90 | | | 91 | | impl<T: ?Sized> NonStaticAny for PhantomData<T> { | 92 | | fn get_type_id(&self) -> TypeId | 93 | | where | 94 | | Self: 'static, | 95 | | { | 96 | | TypeId::of::<T>() | 97 | | } | 98 | | } | 99 | | | 100 | 4.74k | let phantom_data = PhantomData::<T>; | 101 | 4.74k | NonStaticAny::get_type_id(unsafe { | 102 | 4.74k | mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data) | 103 | 4.74k | }) | 104 | 4.74k | } |
unty::non_static_type_id::<roundtrip::AllTypes> Line | Count | Source | 84 | 22.1k | fn non_static_type_id<T: ?Sized>() -> TypeId { | 85 | | trait NonStaticAny { | 86 | | fn get_type_id(&self) -> TypeId | 87 | | where | 88 | | Self: 'static; | 89 | | } | 90 | | | 91 | | impl<T: ?Sized> NonStaticAny for PhantomData<T> { | 92 | | fn get_type_id(&self) -> TypeId | 93 | | where | 94 | | Self: 'static, | 95 | | { | 96 | | TypeId::of::<T>() | 97 | | } | 98 | | } | 99 | | | 100 | 22.1k | let phantom_data = PhantomData::<T>; | 101 | 22.1k | NonStaticAny::get_type_id(unsafe { | 102 | 22.1k | mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data) | 103 | 22.1k | }) | 104 | 22.1k | } |
unty::non_static_type_id::<u8> Line | Count | Source | 84 | 29.6k | fn non_static_type_id<T: ?Sized>() -> TypeId { | 85 | | trait NonStaticAny { | 86 | | fn get_type_id(&self) -> TypeId | 87 | | where | 88 | | Self: 'static; | 89 | | } | 90 | | | 91 | | impl<T: ?Sized> NonStaticAny for PhantomData<T> { | 92 | | fn get_type_id(&self) -> TypeId | 93 | | where | 94 | | Self: 'static, | 95 | | { | 96 | | TypeId::of::<T>() | 97 | | } | 98 | | } | 99 | | | 100 | 29.6k | let phantom_data = PhantomData::<T>; | 101 | 29.6k | NonStaticAny::get_type_id(unsafe { | 102 | 29.6k | mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data) | 103 | 29.6k | }) | 104 | 29.6k | } |
|
105 | | |
106 | | #[test] |
107 | | fn test_double_drop() { |
108 | | use core::sync::atomic::{AtomicUsize, Ordering}; |
109 | | #[derive(Debug)] |
110 | | struct Ty; |
111 | | static COUNTER: AtomicUsize = AtomicUsize::new(0); |
112 | | |
113 | | impl Drop for Ty { |
114 | | fn drop(&mut self) { |
115 | | COUNTER.fetch_add(1, Ordering::Relaxed); |
116 | | } |
117 | | } |
118 | | |
119 | | fn foo<T: core::fmt::Debug>(t: T) { |
120 | | unsafe { unty::<T, Ty>(t) }.unwrap(); |
121 | | } |
122 | | |
123 | | foo(Ty); |
124 | | assert_eq!(COUNTER.load(Ordering::Relaxed), 1); |
125 | | } |