From 931aadfa1ae899e521c0c9de532e78b2f4a44949 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Dec 2021 14:19:13 -0800 Subject: [PATCH] implement for &[T] and &mut [T] Implementing for references over arbitrary unsized types doesn't work because it may not be safe to cast between them (I think?). But it's definitely safe to do this for [T]. This makes it possible to perform casts like `cast!(some_slice, &[u8])` (useful for optimizing for byte arrays). --- src/internal.rs | 44 +++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 22 ++++++++++++++++++++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index a0a5f20..520262a 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -2,7 +2,11 @@ //! macros. Some are public so they can be accessed by the expanded macro code, //! but are not meant to be used by users directly and do not have a stable API. -use core::{any::{TypeId, type_name}, marker::PhantomData, mem}; +use core::{ + any::{type_name, TypeId}, + marker::PhantomData, + mem, +}; /// A token struct used to capture the type of a value without taking ownership of /// it. Used to select a cast implementation in macros. @@ -15,6 +19,44 @@ impl CastToken { } } +/// Supporting trait for autoderef specialization on references. +pub trait TryCastSliceMut<'a, T: 'static> { + /// Attempt to cast a generic reference to a given type if the types are + /// equal. + /// + /// The reference does not have to be static as long as the reference target + /// type is static. + #[inline(always)] + fn try_cast(&self, value: &'a mut [T]) -> Result<&'a mut [U], &'a mut [T]> { + if type_eq::() { + Ok(unsafe { &mut *(value as *mut [T] as *mut [U]) }) + } else { + Err(value) + } + } +} + +impl<'a, T: 'static> TryCastSliceMut<'a, T> for &&&&CastToken<&'a mut [T]> {} + +/// Supporting trait for autoderef specialization on references. +pub trait TryCastSliceRef<'a, T: 'static> { + /// Attempt to cast a generic reference to a given type if the types are + /// equal. + /// + /// The reference does not have to be static as long as the reference target + /// type is static. + #[inline(always)] + fn try_cast(&self, value: &'a [T]) -> Result<&'a [U], &'a [T]> { + if type_eq::() { + Ok(unsafe { &*(value as *const [T] as *const [U]) }) + } else { + Err(value) + } + } +} + +impl<'a, T: 'static> TryCastSliceRef<'a, T> for &&&CastToken<&'a [T]> {} + /// Supporting trait for autoderef specialization on mutable references. pub trait TryCastMut<'a, T: 'static> { /// Attempt to cast a generic mutable reference to a given type if the types diff --git a/src/lib.rs b/src/lib.rs index d6e936c..336f02b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,7 +125,9 @@ pub mod internal; macro_rules! cast { ($value:expr, $T:ty) => {{ #[allow(unused_imports)] - use $crate::internal::{CastToken, TryCastMut, TryCastOwned, TryCastRef}; + use $crate::internal::{ + CastToken, TryCastMut, TryCastOwned, TryCastRef, TryCastSliceMut, TryCastSliceRef, + }; // Here we are using an _autoderef specialization_ technique, which // exploits method resolution autoderefs to select different cast @@ -139,7 +141,7 @@ macro_rules! cast { // thus are preferred by the compiler if applicable. let value = $value; let token = CastToken::of_val(&value); - let result: ::core::result::Result<$T, _> = (&&&token).try_cast(value); + let result: ::core::result::Result<$T, _> = (&&&&&token).try_cast(value); result }}; @@ -249,6 +251,22 @@ mod tests { } inner(&value); + + let mut slice = [1u8; 2]; + + fn inner2<'a>(value: &'a [u8]) { + assert_eq!(cast!(value, &[u8]), Ok(&[1, 1][..])); + assert_eq!(cast!(value, &'a [u8]), Ok(&[1, 1][..])); + assert_eq!(cast!(value, &'a [u16]), Err(&[1, 1][..])); + assert_eq!(cast!(value, &'a [i8]), Err(&[1, 1][..])); + } + + inner2(&slice); + + fn inner3<'a>(value: &'a mut [u8]) { + assert_eq!(cast!(value, &mut [u8]), Ok(&mut [1, 1][..])); + } + inner3(&mut slice); } #[test]