#![no_std]
use core::any::TypeId;
use core::mem::transmute;
#[inline(always)]
pub fn is_same<T: 'static, U: 'static>() -> bool {
TypeId::of::<T>() == TypeId::of::<U>()
}
#[track_caller]
#[inline(always)]
pub fn assert_same<T: 'static, U: 'static>() {
assert_eq!(TypeId::of::<T>(), TypeId::of::<U>());
}
pub trait Coerce<U> {
fn coerce(self) -> U;
}
impl<'a, T: 'static, U: 'static> Coerce<&'a U> for &'a T {
#[inline(always)]
#[track_caller]
fn coerce(self) -> &'a U {
assert_same::<T, U>();
unsafe { transmute(self) }
}
}
impl<'a, T: 'static, U: 'static> Coerce<&'a mut U> for &'a mut T {
#[inline(always)]
#[track_caller]
fn coerce(self) -> &'a mut U {
assert_same::<T, U>();
unsafe { transmute(self) }
}
}
impl<'a, T: 'static, U: 'static> Coerce<&'a [U]> for &'a [T] {
#[inline(always)]
#[track_caller]
fn coerce(self) -> &'a [U] {
assert_same::<T, U>();
unsafe { transmute(self) }
}
}
impl<'a, T: 'static, U: 'static> Coerce<&'a mut [U]> for &'a mut [T] {
#[inline(always)]
#[track_caller]
fn coerce(self) -> &'a mut [U] {
assert_same::<T, U>();
unsafe { transmute(self) }
}
}
#[inline(always)]
pub fn coerce<T: Coerce<U>, U>(value: T) -> U {
value.coerce()
}
#[inline(always)]
pub fn coerce_static<T: 'static, U: 'static>(value: T) -> U {
assert_same::<T, U>();
unsafe { core::mem::transmute_copy(&core::mem::ManuallyDrop::new(value)) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coerce() {
let mut ints = [0, 1, 2u32];
let mut floats = [0.0, 1.0, 2.0f64];
pub fn generic_fn<T: 'static>(factor: T, slice: &mut [T]) {
if is_same::<u32, T>() {
let slice: &mut [u32] = slice.coerce();
let factor: u32 = coerce_static(factor);
for x in slice {
*x = 2 * factor * *x;
}
} else if is_same::<f64, T>() {
let slice: &mut [f64] = slice.coerce();
let factor: f64 = coerce_static(factor);
for x in slice {
*x = factor * *x;
}
}
}
generic_fn(2, &mut ints);
generic_fn(2.0, &mut floats);
assert_eq!(ints, [0, 4, 8]);
assert_eq!(floats, [0.0, 2.0, 4.0]);
}
}