From c0c3b38025519ff21f82bda99598b361064a4cac Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 4 Feb 2020 00:18:22 -0700 Subject: [PATCH 1/4] eliminate some dead branches --- src/lib.rs | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7cfb450..dec77d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,11 +38,12 @@ //! is always built with `extern_crate_alloc` cargo feature enabled. #[cfg(target_arch = "x86")] -pub(crate) use core::arch::x86; +use core::arch::x86; #[cfg(target_arch = "x86_64")] -pub(crate) use core::arch::x86_64; +use core::arch::x86_64; // -pub(crate) use core::{marker::*, mem::*, num::*, ptr::*}; +use core::{marker::*, mem::*, num::*, ptr::*}; +use core::hint::unreachable_unchecked; macro_rules! impl_unsafe_marker_for_array { ( $marker:ident , $( $n:expr ),* ) => { @@ -81,7 +82,9 @@ pub use ::core as __core; /// empty slice might not match the pointer value of the input reference. #[inline] pub fn bytes_of(t: &T) -> &[u8] { - try_cast_slice::(core::slice::from_ref(t)).unwrap_or(&[]) + // We never fail to go from T to u8s, so we hint the compiler about it. + try_cast_slice::(core::slice::from_ref(t)) + .unwrap_or_else(|_| unsafe { unreachable_unchecked() }) } /// Re-interprets `&mut T` as `&mut [u8]`. @@ -90,7 +93,9 @@ pub fn bytes_of(t: &T) -> &[u8] { /// empty slice might not match the pointer value of the input reference. #[inline] pub fn bytes_of_mut(t: &mut T) -> &mut [u8] { - try_cast_slice_mut::(core::slice::from_mut(t)).unwrap_or(&mut []) + // We never fail to go from T to u8s, so we hint the compiler about it. + try_cast_slice_mut::(core::slice::from_mut(t)) + .unwrap_or_else(|_| unsafe { unreachable_unchecked() }) } /// Re-interprets `&[u8]` as `&T`. @@ -175,7 +180,12 @@ pub enum PodCastError { /// This is [`try_cast`] with an unwrap. #[inline] pub fn cast(a: A) -> B { - try_cast(a).unwrap() + if size_of::() == size_of::() { + // In this case it will not fail, and so we hint the compiler. + try_cast(a).unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + } else { + try_cast(a).unwrap() + } } /// Cast `&mut T` into `&mut U`. @@ -185,7 +195,12 @@ pub fn cast(a: A) -> B { /// This is [`try_cast_mut`] with an unwrap. #[inline] pub fn cast_mut(a: &mut A) -> &mut B { - try_cast_mut(a).unwrap() + if size_of::() == size_of::() && align_of::() >= align_of::() { + // In this case it will not fail, and so we hint the compiler. + try_cast_mut(a).unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + } else { + try_cast_mut(a).unwrap() + } } /// Cast `&T` into `&U`. @@ -195,7 +210,12 @@ pub fn cast_mut(a: &mut A) -> &mut B { /// This is [`try_cast_ref`] with an unwrap. #[inline] pub fn cast_ref(a: &A) -> &B { - try_cast_ref(a).unwrap() + if size_of::() == size_of::() && align_of::() >= align_of::() { + // In this case it will not fail, and so we hint the compiler. + try_cast_ref(a).unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + } else { + try_cast_ref(a).unwrap() + } } /// Cast `&[T]` into `&[U]`. @@ -357,14 +377,15 @@ pub fn try_cast_slice_mut( } /// Find the offset in bytes of the given `$field` of `$Type`, using `$instance` -/// as an already-initialized reference value. +/// as an already-initialized value to work with. /// /// This is similar to the macro from `memoffset`, however it's fully well /// defined even in current versions of Rust (and uses no unsafe code). /// -/// It does by using the `$instance` argument to get an already-initialized +/// It does by using the `$instance` argument to have an already-initialized /// instance of `$Type` rather than trying to find a way access the fields of an -/// uninitialized one without hitting soundness problems. +/// uninitialized one without hitting soundness problems. The value passed to +/// the macro is referenced but not moved. /// /// This means the API is more limited, but it's also sound even in rather /// extreme cases, like some of the examples. @@ -410,7 +431,7 @@ pub fn try_cast_slice_mut( /// ### Use with other types /// /// More esoteric uses are possible too, including with types generally not safe -/// to bytemuck. `Strings`, `Vec`s, etc. +/// to otherwise use with bytemuck. `Strings`, `Vec`s, etc. /// /// ``` /// #[derive(Default)] From 326c78d127eb4885e585315737a8e47c3950ce8e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 4 Feb 2020 00:20:12 -0700 Subject: [PATCH 2/4] formatting --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dec77d9..f78d024 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,8 +42,7 @@ use core::arch::x86; #[cfg(target_arch = "x86_64")] use core::arch::x86_64; // -use core::{marker::*, mem::*, num::*, ptr::*}; -use core::hint::unreachable_unchecked; +use core::{hint::unreachable_unchecked, marker::*, mem::*, num::*, ptr::*}; macro_rules! impl_unsafe_marker_for_array { ( $marker:ident , $( $n:expr ),* ) => { From cd8ed37eae289ab5cc9895d7e7084ef8b53b7724 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 6 Feb 2020 19:18:10 -0700 Subject: [PATCH 3/4] Remove the unreachable_unchecked --- src/lib.rs | 213 ++++++++++++++++++----------------------------- src/offset_of.rs | 103 +++++++++++++++++++++++ 2 files changed, 182 insertions(+), 134 deletions(-) create mode 100644 src/offset_of.rs diff --git a/src/lib.rs b/src/lib.rs index f78d024..801bacc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,13 @@ use core::arch::x86; #[cfg(target_arch = "x86_64")] use core::arch::x86_64; // -use core::{hint::unreachable_unchecked, marker::*, mem::*, num::*, ptr::*}; +use core::{marker::*, mem::*, num::*, ptr::*}; + +// Used from macros to ensure we aren't using some locally defined name and +// actually are referencing libcore. This also would allow pre-2018 edition +// crates to use our macros, but I'm not sure how important that is. +#[doc(hidden)] +pub use ::core as __core; macro_rules! impl_unsafe_marker_for_array { ( $marker:ident , $( $n:expr ),* ) => { @@ -66,14 +72,23 @@ pub use pod::*; mod contiguous; pub use contiguous::*; +mod offset_of; +pub use offset_of::*; + mod transparent; pub use transparent::*; -// Used from macros to ensure we aren't using some locally defined name and -// actually are referencing libcore. This also would allow pre-2018 edition -// crates to use our macros, but I'm not sure how important that is. -#[doc(hidden)] -pub use ::core as __core; +/* + +Note(Lokathor): We've switched all of the `unwrap` to `match` because there is +apparently a bug: https://github.com/rust-lang/rust/issues/68667 +and it doesn't seem to show up in simple godbolt examples but has been reported +as having an impact when there's a cast mixed in with other more complicated +code around it. Rustc/LLVM ends up missing that the `Err` can't ever happen for +particular type combinations, and then it doesn't fully eliminated the panic +possibility code branch. + +*/ /// Re-interprets `&T` as `&[u8]`. /// @@ -81,9 +96,10 @@ pub use ::core as __core; /// empty slice might not match the pointer value of the input reference. #[inline] pub fn bytes_of(t: &T) -> &[u8] { - // We never fail to go from T to u8s, so we hint the compiler about it. - try_cast_slice::(core::slice::from_ref(t)) - .unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + match try_cast_slice::(core::slice::from_ref(t)) { + Ok(s) => s, + Err(_) => unreachable!(), + } } /// Re-interprets `&mut T` as `&mut [u8]`. @@ -92,29 +108,36 @@ pub fn bytes_of(t: &T) -> &[u8] { /// empty slice might not match the pointer value of the input reference. #[inline] pub fn bytes_of_mut(t: &mut T) -> &mut [u8] { - // We never fail to go from T to u8s, so we hint the compiler about it. - try_cast_slice_mut::(core::slice::from_mut(t)) - .unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + match try_cast_slice_mut::(core::slice::from_mut(t)) { + Ok(s) => s, + Err(_) => unreachable!(), + } } /// Re-interprets `&[u8]` as `&T`. /// /// ## Panics /// -/// This is [`try_from_bytes`] with an unwrap. +/// This is [`try_from_bytes`] but will panic on error. #[inline] pub fn from_bytes(s: &[u8]) -> &T { - try_from_bytes(s).unwrap() + match try_from_bytes(s) { + Ok(t) => t, + Err(e) => panic!("from_bytes>{:?}", e), + } } /// Re-interprets `&mut [u8]` as `&mut T`. /// /// ## Panics /// -/// This is [`try_from_bytes_mut`] with an unwrap. +/// This is [`try_from_bytes_mut`] but will panic on error. #[inline] pub fn from_bytes_mut(s: &mut [u8]) -> &mut T { - try_from_bytes_mut(s).unwrap() + match try_from_bytes_mut(s) { + Ok(t) => t, + Err(e) => panic!("from_bytes_mut>{:?}", e), + } } /// Re-interprets `&[u8]` as `&T`. @@ -176,14 +199,20 @@ pub enum PodCastError { /// /// ## Panics /// -/// This is [`try_cast`] with an unwrap. +/// This is [`try_cast`] but will panic on error. #[inline] pub fn cast(a: A) -> B { if size_of::() == size_of::() { - // In this case it will not fail, and so we hint the compiler. - try_cast(a).unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } } else { - try_cast(a).unwrap() + match try_cast(a) { + Ok(b) => b, + Err(e) => panic!("cast>{:?}", e), + } } } @@ -191,14 +220,20 @@ pub fn cast(a: A) -> B { /// /// ## Panics /// -/// This is [`try_cast_mut`] with an unwrap. +/// This is [`try_cast_mut`] but will panic on error. #[inline] pub fn cast_mut(a: &mut A) -> &mut B { if size_of::() == size_of::() && align_of::() >= align_of::() { - // In this case it will not fail, and so we hint the compiler. - try_cast_mut(a).unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast_mut(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } } else { - try_cast_mut(a).unwrap() + match try_cast_mut(a) { + Ok(b) => b, + Err(e) => panic!("cast_mut>{:?}", e), + } } } @@ -206,14 +241,20 @@ pub fn cast_mut(a: &mut A) -> &mut B { /// /// ## Panics /// -/// This is [`try_cast_ref`] with an unwrap. +/// This is [`try_cast_ref`] but will panic on error. #[inline] pub fn cast_ref(a: &A) -> &B { if size_of::() == size_of::() && align_of::() >= align_of::() { - // In this case it will not fail, and so we hint the compiler. - try_cast_ref(a).unwrap_or_else(|_| unsafe { unreachable_unchecked() }) + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast_ref(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } } else { - try_cast_ref(a).unwrap() + match try_cast_ref(a) { + Ok(b) => b, + Err(e) => panic!("cast_ref>{:?}", e), + } } } @@ -221,20 +262,26 @@ pub fn cast_ref(a: &A) -> &B { /// /// ## Panics /// -/// This is [`try_cast_slice`] with an unwrap. +/// This is [`try_cast_slice`] but will panic on error. #[inline] pub fn cast_slice(a: &[A]) -> &[B] { - try_cast_slice(a).unwrap() + match try_cast_slice(a) { + Ok(b) => b, + Err(e) => panic!("cast_slice>{:?}", e), + } } /// Cast `&mut [T]` into `&mut [U]`. /// /// ## Panics /// -/// This is [`try_cast_slice_mut`] with an unwrap. +/// This is [`try_cast_slice_mut`] but will panic on error. #[inline] pub fn cast_slice_mut(a: &mut [A]) -> &mut [B] { - try_cast_slice_mut(a).unwrap() + match try_cast_slice_mut(a) { + Ok(b) => b, + Err(e) => panic!("cast_slice_mut>{:?}", e), + } } /// As `align_to`, but safe because of the [`Pod`] bound. @@ -374,105 +421,3 @@ pub fn try_cast_slice_mut( Err(PodCastError::OutputSliceWouldHaveSlop) } } - -/// Find the offset in bytes of the given `$field` of `$Type`, using `$instance` -/// as an already-initialized value to work with. -/// -/// This is similar to the macro from `memoffset`, however it's fully well -/// defined even in current versions of Rust (and uses no unsafe code). -/// -/// It does by using the `$instance` argument to have an already-initialized -/// instance of `$Type` rather than trying to find a way access the fields of an -/// uninitialized one without hitting soundness problems. The value passed to -/// the macro is referenced but not moved. -/// -/// This means the API is more limited, but it's also sound even in rather -/// extreme cases, like some of the examples. -/// -/// ## Caveats -/// -/// 1. The offset is in bytes, and so you will likely have to cast your base -/// pointers to `*const u8`/`*mut u8` before getting field addresses. -/// -/// 2. The offset values of repr(Rust) types are not stable, and may change -/// wildly between releases of the compiler. Use repr(C) if you can. -/// -/// 3. The value of the `$instance` parameter has no bearing on the output of -/// this macro. It is just used to avoid soundness problems. The only -/// requirement is that it be initialized. In particular, the value returned -/// is not a field pointer, or anything like that. -/// -/// ## Examples -/// -/// ### Use with zeroable types -/// A common requirement in GPU apis is to specify the layout of vertices. These -/// will generally be [`Zeroable`] (if not [`Pod`]), and are a good fit for -/// `offset_of!`. -/// ``` -/// # use bytemuck::{Zeroable, offset_of}; -/// #[repr(C)] -/// struct Vertex { -/// pos: [f32; 2], -/// uv: [u16; 2], -/// color: [u8; 4], -/// } -/// unsafe impl Zeroable for Vertex {} -/// -/// let pos = offset_of!(Zeroable::zeroed(), Vertex, pos); -/// let uv = offset_of!(Zeroable::zeroed(), Vertex, uv); -/// let color = offset_of!(Zeroable::zeroed(), Vertex, color); -/// -/// assert_eq!(pos, 0); -/// assert_eq!(uv, 8); -/// assert_eq!(color, 12); -/// ``` -/// -/// ### Use with other types -/// -/// More esoteric uses are possible too, including with types generally not safe -/// to otherwise use with bytemuck. `Strings`, `Vec`s, etc. -/// -/// ``` -/// #[derive(Default)] -/// struct Foo { -/// a: u8, -/// b: &'static str, -/// c: i32, -/// } -/// -/// let a_offset = bytemuck::offset_of!(Default::default(), Foo, a); -/// let b_offset = bytemuck::offset_of!(Default::default(), Foo, b); -/// let c_offset = bytemuck::offset_of!(Default::default(), Foo, c); -/// -/// assert_ne!(a_offset, b_offset); -/// assert_ne!(b_offset, c_offset); -/// // We can't check against hardcoded values for a repr(Rust) type, -/// // but prove to ourself this way. -/// -/// let foo = Foo::default(); -/// // Note: offsets are in bytes. -/// let as_bytes = &foo as *const _ as *const u8; -/// -/// // we're using wrapping_offset here becasue it's not worth -/// // the unsafe block, but it would be valid to use `add` instead, -/// // as it cannot overflow. -/// assert_eq!(&foo.a as *const _ as usize, as_bytes.wrapping_add(a_offset) as usize); -/// assert_eq!(&foo.b as *const _ as usize, as_bytes.wrapping_add(b_offset) as usize); -/// assert_eq!(&foo.c as *const _ as usize, as_bytes.wrapping_add(c_offset) as usize); -/// ``` -#[macro_export] -macro_rules! offset_of { - ($instance:expr, $Type:path, $field:tt) => {{ - // This helps us guard against field access going through a Deref impl. - #[allow(clippy::unneeded_field_pattern)] - let $Type { $field: _, .. }; - let reference: &$Type = &$instance; - let address = reference as *const _ as usize; - let field_pointer = &reference.$field as *const _ as usize; - // These asserts/unwraps are compiled away at release, and defend against - // the case where somehow a deref impl is still invoked. - let result = field_pointer.checked_sub(address).unwrap(); - assert!(result <= $crate::__core::mem::size_of::<$Type>()); - result - }}; -} diff --git a/src/offset_of.rs b/src/offset_of.rs new file mode 100644 index 0000000..fa85727 --- /dev/null +++ b/src/offset_of.rs @@ -0,0 +1,103 @@ +#![forbid(unsafe_code)] + +/// Find the offset in bytes of the given `$field` of `$Type`, using `$instance` +/// as an already-initialized value to work with. +/// +/// This is similar to the macro from `memoffset`, however it's fully well +/// defined even in current versions of Rust (and uses no unsafe code). +/// +/// It does by using the `$instance` argument to have an already-initialized +/// instance of `$Type` rather than trying to find a way access the fields of an +/// uninitialized one without hitting soundness problems. The value passed to +/// the macro is referenced but not moved. +/// +/// This means the API is more limited, but it's also sound even in rather +/// extreme cases, like some of the examples. +/// +/// ## Caveats +/// +/// 1. The offset is in bytes, and so you will likely have to cast your base +/// pointers to `*const u8`/`*mut u8` before getting field addresses. +/// +/// 2. The offset values of repr(Rust) types are not stable, and may change +/// wildly between releases of the compiler. Use repr(C) if you can. +/// +/// 3. The value of the `$instance` parameter has no bearing on the output of +/// this macro. It is just used to avoid soundness problems. The only +/// requirement is that it be initialized. In particular, the value returned +/// is not a field pointer, or anything like that. +/// +/// ## Examples +/// +/// ### Use with zeroable types +/// A common requirement in GPU apis is to specify the layout of vertices. These +/// will generally be [`Zeroable`] (if not [`Pod`]), and are a good fit for +/// `offset_of!`. +/// ``` +/// # use bytemuck::{Zeroable, offset_of}; +/// #[repr(C)] +/// struct Vertex { +/// pos: [f32; 2], +/// uv: [u16; 2], +/// color: [u8; 4], +/// } +/// unsafe impl Zeroable for Vertex {} +/// +/// let pos = offset_of!(Zeroable::zeroed(), Vertex, pos); +/// let uv = offset_of!(Zeroable::zeroed(), Vertex, uv); +/// let color = offset_of!(Zeroable::zeroed(), Vertex, color); +/// +/// assert_eq!(pos, 0); +/// assert_eq!(uv, 8); +/// assert_eq!(color, 12); +/// ``` +/// +/// ### Use with other types +/// +/// More esoteric uses are possible too, including with types generally not safe +/// to otherwise use with bytemuck. `Strings`, `Vec`s, etc. +/// +/// ``` +/// #[derive(Default)] +/// struct Foo { +/// a: u8, +/// b: &'static str, +/// c: i32, +/// } +/// +/// let a_offset = bytemuck::offset_of!(Default::default(), Foo, a); +/// let b_offset = bytemuck::offset_of!(Default::default(), Foo, b); +/// let c_offset = bytemuck::offset_of!(Default::default(), Foo, c); +/// +/// assert_ne!(a_offset, b_offset); +/// assert_ne!(b_offset, c_offset); +/// // We can't check against hardcoded values for a repr(Rust) type, +/// // but prove to ourself this way. +/// +/// let foo = Foo::default(); +/// // Note: offsets are in bytes. +/// let as_bytes = &foo as *const _ as *const u8; +/// +/// // we're using wrapping_offset here becasue it's not worth +/// // the unsafe block, but it would be valid to use `add` instead, +/// // as it cannot overflow. +/// assert_eq!(&foo.a as *const _ as usize, as_bytes.wrapping_add(a_offset) as usize); +/// assert_eq!(&foo.b as *const _ as usize, as_bytes.wrapping_add(b_offset) as usize); +/// assert_eq!(&foo.c as *const _ as usize, as_bytes.wrapping_add(c_offset) as usize); +/// ``` +#[macro_export] +macro_rules! offset_of { + ($instance:expr, $Type:path, $field:tt) => {{ + // This helps us guard against field access going through a Deref impl. + #[allow(clippy::unneeded_field_pattern)] + let $Type { $field: _, .. }; + let reference: &$Type = &$instance; + let address = reference as *const _ as usize; + let field_pointer = &reference.$field as *const _ as usize; + // These asserts/unwraps are compiled away at release, and defend against + // the case where somehow a deref impl is still invoked. + let result = field_pointer.checked_sub(address).unwrap(); + assert!(result <= $crate::__core::mem::size_of::<$Type>()); + result + }}; +} From fa686336e7b9a2bcba02a40ad17ce3ec6d72ceec Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 6 Feb 2020 19:38:05 -0700 Subject: [PATCH 4/4] factor the panic to one spot. --- src/lib.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 801bacc..a90199d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,16 @@ possibility code branch. */ +/// Immediately panics. +#[cold] +#[inline(never)] +fn something_went_wrong(src: &str, err: PodCastError) -> ! { + // Note(Lokathor): Keeping the panic here makes the panic _formatting_ go + // here too, which helps assembly readability and also helps keep down + // the inline pressure. + panic!("{src}>{err:?}", src = src, err = err) +} + /// Re-interprets `&T` as `&[u8]`. /// /// Any ZST becomes an empty slice, and in that case the pointer value of that @@ -123,7 +133,7 @@ pub fn bytes_of_mut(t: &mut T) -> &mut [u8] { pub fn from_bytes(s: &[u8]) -> &T { match try_from_bytes(s) { Ok(t) => t, - Err(e) => panic!("from_bytes>{:?}", e), + Err(e) => something_went_wrong("from_bytes", e), } } @@ -136,7 +146,7 @@ pub fn from_bytes(s: &[u8]) -> &T { pub fn from_bytes_mut(s: &mut [u8]) -> &mut T { match try_from_bytes_mut(s) { Ok(t) => t, - Err(e) => panic!("from_bytes_mut>{:?}", e), + Err(e) => something_went_wrong("from_bytes_mut", e), } } @@ -211,7 +221,7 @@ pub fn cast(a: A) -> B { } else { match try_cast(a) { Ok(b) => b, - Err(e) => panic!("cast>{:?}", e), + Err(e) => something_went_wrong("cast", e), } } } @@ -232,7 +242,7 @@ pub fn cast_mut(a: &mut A) -> &mut B { } else { match try_cast_mut(a) { Ok(b) => b, - Err(e) => panic!("cast_mut>{:?}", e), + Err(e) => something_went_wrong("cast_mut", e), } } } @@ -253,7 +263,7 @@ pub fn cast_ref(a: &A) -> &B { } else { match try_cast_ref(a) { Ok(b) => b, - Err(e) => panic!("cast_ref>{:?}", e), + Err(e) => something_went_wrong("cast_ref", e), } } } @@ -267,7 +277,7 @@ pub fn cast_ref(a: &A) -> &B { pub fn cast_slice(a: &[A]) -> &[B] { match try_cast_slice(a) { Ok(b) => b, - Err(e) => panic!("cast_slice>{:?}", e), + Err(e) => something_went_wrong("cast_slice", e), } } @@ -280,7 +290,7 @@ pub fn cast_slice(a: &[A]) -> &[B] { pub fn cast_slice_mut(a: &mut [A]) -> &mut [B] { match try_cast_slice_mut(a) { Ok(b) => b, - Err(e) => panic!("cast_slice_mut>{:?}", e), + Err(e) => something_went_wrong("cast_slice_mut", e), } }