Coverage Report

Created: 2025-11-24 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}