coe/
lib.rs

1//! `coe-rs` is a Rust library for coercing a value of a given type into the same type, in cases
2//! where the compiler can't prove the two types are equal.  
3//! This can be used to emulate specialization in to a limited extent.
4#![no_std]
5
6use core::any::TypeId;
7use core::mem::transmute;
8
9/// Returns `true` if `T` and `U` are the same type.
10#[inline(always)]
11pub fn is_same<T: 'static, U: 'static>() -> bool {
12    TypeId::of::<T>() == TypeId::of::<U>()
13}
14
15/// Checks if `T` and `U` are the same type, and panics if that's not the case.
16#[track_caller]
17#[inline(always)]
18pub fn assert_same<T: 'static, U: 'static>() {
19    assert_eq!(TypeId::of::<T>(), TypeId::of::<U>());
20}
21
22/// Trait for performing coercion from one type to another, where the types
23/// are identical but the compiler can't prove it.
24///
25/// # Example
26/// ```
27/// use coe::{Coerce, is_same};
28/// use core::ops::Add;
29///
30/// fn foo<T: 'static + Copy + Add<Output = T>>(slice: &mut [T]) {
31///     if is_same::<f64, T>() {
32///         // use some optimized SIMD implementation
33///         // ...
34///         println!("using SIMD operations");
35///         let slice: &mut [f64] = slice.coerce();
36///     } else {
37///         for value in slice {
38///             println!("using fallback implementation");
39///             *value = *value + *value;
40///         }
41///     }
42/// }
43///
44/// foo(&mut [1, 2, 3u64]); // calls fallback implementation
45/// foo(&mut [1.0, 2.0, 3.0f64]); // calls SIMD implementation
46/// ```
47pub trait Coerce<U> {
48    fn coerce(self) -> U;
49}
50
51impl<'a, T: 'static, U: 'static> Coerce<&'a U> for &'a T {
52    #[inline(always)]
53    #[track_caller]
54    fn coerce(self) -> &'a U {
55        assert_same::<T, U>();
56        unsafe { transmute(self) }
57    }
58}
59
60impl<'a, T: 'static, U: 'static> Coerce<&'a mut U> for &'a mut T {
61    #[inline(always)]
62    #[track_caller]
63    fn coerce(self) -> &'a mut U {
64        assert_same::<T, U>();
65        unsafe { transmute(self) }
66    }
67}
68
69impl<'a, T: 'static, U: 'static> Coerce<&'a [U]> for &'a [T] {
70    #[inline(always)]
71    #[track_caller]
72    fn coerce(self) -> &'a [U] {
73        assert_same::<T, U>();
74        unsafe { transmute(self) }
75    }
76}
77
78impl<'a, T: 'static, U: 'static> Coerce<&'a mut [U]> for &'a mut [T] {
79    #[inline(always)]
80    #[track_caller]
81    fn coerce(self) -> &'a mut [U] {
82        assert_same::<T, U>();
83        unsafe { transmute(self) }
84    }
85}
86
87/// Free function that defers to the `Coerce` trait implementation.
88#[inline(always)]
89pub fn coerce<T: Coerce<U>, U>(value: T) -> U {
90    value.coerce()
91}
92
93/// Similar to [`coerce`] but operates on any lifetime-free type.
94#[inline(always)]
95pub fn coerce_static<T: 'static, U: 'static>(value: T) -> U {
96    assert_same::<T, U>();
97    unsafe { core::mem::transmute_copy(&core::mem::ManuallyDrop::new(value)) }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_coerce() {
106        let mut ints = [0, 1, 2u32];
107        let mut floats = [0.0, 1.0, 2.0f64];
108
109        pub fn generic_fn<T: 'static>(factor: T, slice: &mut [T]) {
110            if is_same::<u32, T>() {
111                let slice: &mut [u32] = slice.coerce();
112                let factor: u32 = coerce_static(factor);
113                for x in slice {
114                    *x = 2 * factor * *x;
115                }
116            } else if is_same::<f64, T>() {
117                let slice: &mut [f64] = slice.coerce();
118                let factor: f64 = coerce_static(factor);
119                for x in slice {
120                    *x = factor * *x;
121                }
122            }
123        }
124
125        generic_fn(2, &mut ints);
126        generic_fn(2.0, &mut floats);
127
128        assert_eq!(ints, [0, 4, 8]);
129        assert_eq!(floats, [0.0, 2.0, 4.0]);
130    }
131}