Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement for &[T] and &mut [T] #2

Merged
merged 1 commit into from
Dec 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -15,6 +19,44 @@ impl<T> CastToken<T> {
}
}

/// 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<U: 'static>(&self, value: &'a mut [T]) -> Result<&'a mut [U], &'a mut [T]> {
if type_eq::<T, U>() {
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<U: 'static>(&self, value: &'a [T]) -> Result<&'a [U], &'a [T]> {
if type_eq::<T, U>() {
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
Expand Down
22 changes: 20 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}};
Expand Down Expand Up @@ -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]
Expand Down