// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT // // Copyright 2025 The Fuchsia Authors // // Licensed under the 2-Clause BSD License , Apache License, Version 2.0 // , or the MIT // license , at your option. // This file may not be copied, modified, or distributed except according to // those terms. use super::*; use crate::pointer::invariant::{Aligned, Exclusive, Invariants, Safe, Shared}; /// Types that can be split in two. /// /// This trait generalizes Rust's existing support for splitting slices to /// support slices and slice-based dynamically-sized types ("slice DSTs"). /// /// For a `repr(C, packed)` or `repr(C, packed(N))` struct, the derive wraps /// the trailing field's [`Elem`](Self::Elem) in [`Unalign`]. The returned /// slice can therefore be accessed even when packing misaligns the original /// elements. Each packed struct in the trailing-field chain adds one wrapper; /// unpacked structs and [`ManuallyDrop`] forward their trailing type's `Elem`. /// /// # Implementation /// /// **Do not implement this trait yourself!** Instead, use /// [`#[derive(SplitAt)]`][derive]; e.g.: /// /// ``` /// # use zerocopy_derive::{SplitAt, KnownLayout}; /// #[derive(SplitAt, KnownLayout)] /// #[repr(C)] /// struct MyStruct { /// # /* /// ..., /// # */ /// // `SplitAt` types must have at least one field. /// field: T, /// } /// ``` /// /// This derive performs a sophisticated, compile-time safety analysis to /// determine whether a type is `SplitAt`. /// /// A packed struct exposes its trailing elements through `Unalign`: /// /// ``` /// use zerocopy::{FromBytes, SplitAt, Unalign}; /// # use zerocopy_derive::*; /// /// #[derive(FromBytes, IntoBytes, KnownLayout, SplitAt)] /// #[repr(C, packed)] /// struct Packet { /// tag: u8, /// words: [u16], /// } /// /// let mut bytes = [0u8; 5]; /// let packet = Packet::mut_from_bytes(&mut bytes[..]).unwrap(); /// let (left, right): (&mut Packet, &mut [Unalign]) = /// packet.split_at_mut(1).unwrap().via_into_bytes(); /// left.tag = 1; /// right[0] = Unalign::new(0x0202); /// assert_eq!(bytes, [1, 0, 0, 2, 2]); /// ``` /// /// # Safety /// /// This trait does not convey any safety guarantees to code outside this crate. /// /// You must not rely on the `#[doc(hidden)]` internals of `SplitAt`. Future /// releases of zerocopy may make backwards-breaking changes to these items, /// including changes that only affect soundness, which may cause code which /// uses those items to silently become unsound. /// #[cfg_attr(feature = "derive", doc = "[derive]: zerocopy_derive::SplitAt")] #[cfg_attr( not(feature = "derive"), doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.SplitAt.html"), )] #[cfg_attr( not(no_zerocopy_diagnostic_on_unimplemented_1_78_0), diagnostic::on_unimplemented(note = "Consider adding `#[derive(SplitAt)]` to `{Self}`") )] // # Safety // // `Self` is a slice, a `repr(C)` (possibly packed) or `repr(transparent)` slice // DST, or `ManuallyDrop` for some `T: SplitAt`. // // `Self::Elem` has the same size, bit validity, and `UnsafeCell` coverage as // the actual trailing element type. Access through either representation // preserves the other's validity, including writes through mutable references // and interior mutation through shared references. For every aligned `Self`, // its trailing slice's address is aligned for `Self::Elem`. pub unsafe trait SplitAt: KnownLayout { /// The element type exposed by the split's returned slice. /// /// For `[T]`, this is `T`. For a struct, this is the trailing field's `Elem`, /// wrapped in [`Unalign`] if the struct is packed. Nested packed structs /// produce nested `Unalign` wrappers. type Elem; #[doc(hidden)] fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized; /// Unsafely splits `self` in two. /// /// # Safety /// /// The caller promises that `l_len` is not greater than the length of /// `self`'s trailing slice. /// #[doc = codegen_section!( header = "h5", bench = "split_at_unchecked", format = "coco", arity = 2, [ open @index 1 @title "Unsized" @variant "dynamic_size" ], [ @index 2 @title "Dynamically Padded" @variant "dynamic_padding" ] )] #[inline] #[must_use] unsafe fn split_at_unchecked(&self, l_len: usize) -> Split<&Self> { // SAFETY: By precondition on the caller, `l_len <= self.len()`. unsafe { Split::<&Self>::new(self, l_len) } } /// Attempts to split `self` in two. /// /// Returns `None` if `l_len` is greater than the length of `self`'s /// trailing slice. /// /// # Examples /// /// ``` /// use zerocopy::{SplitAt, FromBytes}; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable)] /// #[repr(C)] /// struct Packet { /// length: u8, /// body: [u8], /// } /// /// // These bytes encode a `Packet`. /// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; /// /// let packet = Packet::ref_from_bytes(bytes).unwrap(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// // Attempt to split `packet` at `length`. /// let split = packet.split_at(packet.length as usize).unwrap(); /// /// // Use the `Immutable` bound on `Packet` to prove that it's okay to /// // return concurrent references to `packet` and `rest`. /// let (packet, rest) = split.via_immutable(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// ``` /// #[doc = codegen_section!( header = "h5", bench = "split_at", format = "coco", arity = 2, [ open @index 1 @title "Unsized" @variant "dynamic_size" ], [ @index 2 @title "Dynamically Padded" @variant "dynamic_padding" ] )] #[inline] #[must_use = "has no side effects"] fn split_at(&self, l_len: usize) -> Option> { MetadataOf::new_in_bounds(self, l_len).map( #[inline(always)] |l_len| { // SAFETY: We have ensured that `l_len <= self.len()` (by // post-condition on `MetadataOf::new_in_bounds`) unsafe { Split::new(self, l_len.get()) } }, ) } /// Unsafely splits `self` in two. /// /// # Safety /// /// The caller promises that `l_len` is not greater than the length of /// `self`'s trailing slice. /// #[doc = codegen_header!("h5", "split_at_mut_unchecked")] /// /// See [`SplitAt::split_at_unchecked`](#method.split_at_unchecked.codegen). #[inline] #[must_use] unsafe fn split_at_mut_unchecked(&mut self, l_len: usize) -> Split<&mut Self> { // SAFETY: By precondition on the caller, `l_len <= self.len()`. unsafe { Split::<&mut Self>::new(self, l_len) } } /// Attempts to split `self` in two. /// /// Returns `None` if `l_len` is greater than the length of `self`'s /// trailing slice. /// /// # Examples /// /// ``` /// use zerocopy::{SplitAt, FromBytes}; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes)] /// #[repr(C)] /// struct Packet { /// length: u8, /// body: B, /// } /// /// // These bytes encode a `Packet`. /// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; /// /// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// { /// // Attempt to split `packet` at `length`. /// let split = packet.split_at_mut(packet.length as usize).unwrap(); /// /// // Use the `IntoBytes` bound on `Packet` to prove that it's okay to /// // return concurrent references to `packet` and `rest`. /// let (packet, rest) = split.via_into_bytes(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// /// rest.fill(0); /// } /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]); /// ``` /// #[doc = codegen_header!("h5", "split_at_mut")] /// /// See [`SplitAt::split_at`](#method.split_at.codegen). #[inline] fn split_at_mut(&mut self, l_len: usize) -> Option> { MetadataOf::new_in_bounds(self, l_len).map( #[inline(always)] |l_len| { // SAFETY: We have ensured that `l_len <= self.len()` (by // post-condition on `MetadataOf::new_in_bounds`) unsafe { Split::new(self, l_len.get()) } }, ) } } // SAFETY: `[T]` exposes its own element type, preserving size, validity, and // `UnsafeCell` coverage. An aligned slice is aligned for its elements. unsafe impl SplitAt for [T] { type Elem = T; #[inline] #[allow(dead_code)] fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized, { } } // SAFETY: `ManuallyDrop` has the same layout and bit validity as `T` [1]. // Its `KnownLayout` implementation preserves `T`'s metadata and trailing-slice // layout. Forwarding `T::Elem` therefore preserves its size, validity, and // alignment guarantees. Its `UnsafeCell` coverage also matches `T`'s, as // established for `ManuallyDrop` in `impls.rs`; accesses through the forwarded // element representation preserve validity in both directions. // // [1] Per https://doc.rust-lang.org/1.93.0/std/mem/struct.ManuallyDrop.html: // // `ManuallyDrop` is guaranteed to have the same layout and bit validity as // `T` unsafe impl SplitAt for ManuallyDrop { type Elem = T::Elem; #[inline] #[allow(dead_code)] fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized, { } } /// A `T` that has been split into two possibly-overlapping parts. /// /// For some dynamically sized types, the padding that appears after the /// trailing slice field [is a dynamic function of the trailing slice /// length](KnownLayout#slice-dst-layout). If `T` is split at a length that /// requires trailing padding, the trailing padding of the left part of the /// split `T` will overlap the right part. If `T` is a mutable reference or /// permits interior mutation, you must ensure that the left and right parts do /// not overlap. Use [`Self::via_into_bytes`] or /// [`Self::via_no_dynamic_padding`] to establish this without a runtime check, /// or [`Self::via_runtime_check`] to check a particular split. Shared references /// to an [`Immutable`] type may overlap; use [`Self::via_immutable`] in that /// case. #[derive(Debug)] pub struct Split { /// A pointer to the source slice DST. source: T, /// The length of the future left half of `source`. /// /// # Safety /// /// If `source` is a pointer to a slice DST, `l_len` is no greater than /// `source`'s length. l_len: usize, } impl Split { /// Produces a `Split` of `source` with `l_len`. /// /// # Safety /// /// `l_len` is no greater than `source`'s length. #[inline(always)] unsafe fn new(source: T, l_len: usize) -> Self { Self { source, l_len } } } impl<'a, T> Split<&'a T> where T: ?Sized + SplitAt, { #[inline(always)] fn into_ptr(self) -> Split> { let source = Ptr::from_ref(self.source); // SAFETY: `Ptr::from_ref(self.source)` points to exactly `self.source` // and thus maintains the invariants of `self` with respect to `l_len`. unsafe { Split::new(source, self.l_len) } } /// Produces the split parts of `self`, using [`Immutable`] to ensure that /// it is sound to have concurrent references to both parts. /// /// # Examples /// /// ``` /// use zerocopy::{SplitAt, FromBytes}; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable)] /// #[repr(C)] /// struct Packet { /// length: u8, /// body: [u8], /// } /// /// // These bytes encode a `Packet`. /// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; /// /// let packet = Packet::ref_from_bytes(bytes).unwrap(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// // Attempt to split `packet` at `length`. /// let split = packet.split_at(packet.length as usize).unwrap(); /// /// // Use the `Immutable` bound on `Packet` to prove that it's okay to /// // return concurrent references to `packet` and `rest`. /// let (packet, rest) = split.via_immutable(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// ``` /// #[doc = codegen_section!( header = "h5", bench = "split_via_immutable", format = "coco", arity = 2, [ open @index 1 @title "Unsized" @variant "dynamic_size" ], [ @index 2 @title "Dynamically Padded" @variant "dynamic_padding" ] )] #[must_use = "has no side effects"] #[inline(always)] pub fn via_immutable(self) -> (&'a T, &'a [T::Elem]) where T: Immutable, { let (l, r) = self.into_ptr().via_immutable(); (l.as_ref(), r.as_ref()) } /// Produces the split parts of `self`, using [`IntoBytes`] to ensure that /// it is sound to have concurrent references to both parts. /// /// # Examples /// /// ``` /// use zerocopy::{SplitAt, FromBytes}; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable, IntoBytes)] /// #[repr(C)] /// struct Packet { /// length: u8, /// body: B, /// } /// /// // These bytes encode a `Packet`. /// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; /// /// let packet = Packet::<[u8]>::ref_from_bytes(bytes).unwrap(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// // Attempt to split `packet` at `length`. /// let split = packet.split_at(packet.length as usize).unwrap(); /// /// // Use the `IntoBytes` bound on `Packet` to prove that it's okay to /// // return concurrent references to `packet` and `rest`. /// let (packet, rest) = split.via_into_bytes(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// ``` /// #[doc = codegen_header!("h5", "split_via_into_bytes")] /// /// See [`Split::via_immutable`](#method.split_via_immutable.codegen). #[must_use = "has no side effects"] #[inline(always)] pub fn via_into_bytes(self) -> (&'a T, &'a [T::Elem]) where T: IntoBytes, { let (l, r) = self.into_ptr().via_into_bytes(); (l.as_ref(), r.as_ref()) } /// Produces the split parts of `self` using [`Self::via_no_dynamic_padding`]. #[deprecated(note = "use `Split::via_no_dynamic_padding` instead")] #[doc(hidden)] #[must_use = "has no side effects"] #[inline(always)] pub fn via_unaligned(self) -> (&'a T, &'a [T::Elem]) where T: Unaligned, { self.via_no_dynamic_padding() } /// Produces the split parts of `self` using a compile-time assertion to /// ensure that it is unconditionally sound to have concurrent references to /// both parts. /// /// When possible, you should prefer using [`Self::via_into_bytes`], which /// ensures this safety property through regular trait bounds. /// /// # Examples /// /// In the below example, a single byte of padding exists (on most /// platforms) between `Packet<[u16]>`'s `length` and `body` fields, /// precluding the use of [`Self::via_into_bytes`]. /// /// ``` /// use zerocopy::SplitAt; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, KnownLayout)] /// #[repr(C)] /// struct Packet { /// length: u8, /// body: B, /// } /// /// let packet: &Packet<[u16]> = &Packet { length: 4, body: [1u16, 2, 3, 4, 5, 6, 7, 8, 9] }; /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// if let Some(split) = packet.split_at(packet.length as usize) { /// // Every body length requires no trailing padding. /// let (packet, rest) = split.via_no_dynamic_padding(); /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// } else { /// unreachable!("The packet's length field is within its body"); /// } /// ``` /// /// # Compile-Time Assertions /// /// This method rejects at compile-time any type that admits dynamic /// trailing padding. In the below example, some `split_at` indices would /// produce left parts whose trailing padding overlaps with the right part: /// /// ```compile_fail,E0080 /// use zerocopy::SplitAt; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, KnownLayout)] /// #[repr(C, align(2))] /// struct Packet { /// length: u16, /// body: B, /// } /// /// let packet: &Packet<[u8]> = &Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] }; /// /// if let Some(split) = packet.split_at(packet.length as usize) { /// let _ = split.via_no_dynamic_padding(); // ⚠ Compile Error! /// } else { /// unreachable!("The packet's length field is within its body"); /// } /// ``` /// /// If you need to split such types, use [`Self::via_runtime_check`]. /// #[doc = codegen_header!("h5", "split_via_no_dynamic_padding")] /// /// See [`Split::via_immutable`](#method.split_via_immutable.codegen). #[must_use = "has no side effects"] #[inline(always)] pub fn via_no_dynamic_padding(self) -> (&'a T, &'a [T::Elem]) { let (l, r) = self.into_ptr().via_no_dynamic_padding(); (l.as_ref(), r.as_ref()) } /// Produces the split parts of `self`, using a dynamic check to ensure that /// it is sound to have concurrent references to both parts. You should /// prefer using [`Self::via_immutable`], [`Self::via_into_bytes`], or /// [`Self::via_no_dynamic_padding`], which perform no runtime check. /// /// Note that this check is overly conservative if `T` is [`Immutable`]; for /// some types, this check will reject some splits which /// [`Self::via_immutable`] will accept. /// /// # Examples /// /// ``` /// use zerocopy::SplitAt; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, KnownLayout)] /// #[repr(C, align(2))] /// struct Packet { /// length: u16, /// body: B, /// } /// /// let packet: &Packet<[u8]> = &Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] }; /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// // The two-byte prefix and four-byte body need no trailing padding. /// if let Some(split) = packet.split_at(packet.length as usize) { /// if let Ok((packet, rest)) = split.via_runtime_check() { /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// } else { /// unreachable!("A four-byte body requires no trailing padding"); /// } /// } else { /// unreachable!("The packet's length field is within its body"); /// } /// /// // A three-byte body needs a padding byte that would overlap `rest`. /// if let Some(split) = packet.split_at((packet.length - 1) as usize) { /// assert!(split.via_runtime_check().is_err()); /// } else { /// unreachable!("One less than the packet's length is within its body"); /// } /// ``` /// #[doc = codegen_section!( header = "h5", bench = "split_via_runtime_check", format = "coco", arity = 2, [ open @index 1 @title "Unsized" @variant "dynamic_size" ], [ @index 2 @title "Dynamically Padded" @variant "dynamic_padding" ] )] #[must_use = "has no side effects"] #[inline(always)] pub fn via_runtime_check(self) -> Result<(&'a T, &'a [T::Elem]), Self> { match self.into_ptr().via_runtime_check() { Ok((l, r)) => Ok((l.as_ref(), r.as_ref())), Err(s) => Err(s.into_ref()), } } /// Unsafely produces the split parts of `self`. /// /// # Safety /// /// If `T` permits interior mutation, the trailing padding bytes of the left /// portion must not overlap the right portion. For some dynamically sized /// types, the padding that appears after the trailing slice field [is a /// dynamic function of the trailing slice /// length](KnownLayout#slice-dst-layout). Thus, for some types, this /// condition is dependent on the length of the left portion. /// #[doc = codegen_section!( header = "h5", bench = "split_via_unchecked", format = "coco", arity = 2, [ open @index 1 @title "Unsized" @variant "dynamic_size" ], [ @index 2 @title "Dynamically Padded" @variant "dynamic_padding" ] )] #[must_use = "has no side effects"] #[inline(always)] pub unsafe fn via_unchecked(self) -> (&'a T, &'a [T::Elem]) { // SAFETY: The aliasing of `self.into_ptr()` is not `Exclusive`, but the // caller has promised that if `T` permits interior mutation then the // left and right portions of `self` split at `l_len` do not overlap. let (l, r) = unsafe { self.into_ptr().via_unchecked() }; (l.as_ref(), r.as_ref()) } } impl<'a, T> Split<&'a mut T> where T: ?Sized + SplitAt, { #[inline(always)] fn into_ptr(self) -> Split> { let source = Ptr::from_mut(self.source); // SAFETY: `Ptr::from_mut(self.source)` points to exactly `self.source`, // and thus maintains the invariants of `self` with respect to `l_len`. unsafe { Split::new(source, self.l_len) } } /// Produces the split parts of `self`, using [`IntoBytes`] to ensure that /// it is sound to have concurrent references to both parts. /// /// # Examples /// /// ``` /// use zerocopy::{SplitAt, FromBytes}; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes)] /// #[repr(C)] /// struct Packet { /// length: u8, /// body: B, /// } /// /// // These bytes encode a `Packet`. /// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; /// /// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// { /// // Attempt to split `packet` at `length`. /// let split = packet.split_at_mut(packet.length as usize).unwrap(); /// /// // Use the `IntoBytes` bound on `Packet` to prove that it's okay to /// // return concurrent references to `packet` and `rest`. /// let (packet, rest) = split.via_into_bytes(); /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// /// rest.fill(0); /// } /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]); /// ``` /// /// # Code Generation /// /// See [`Split::via_immutable`](#method.split_via_immutable.codegen). #[must_use = "has no side effects"] #[inline(always)] pub fn via_into_bytes(self) -> (&'a mut T, &'a mut [T::Elem]) where T: IntoBytes, { let (l, r) = self.into_ptr().via_into_bytes(); (l.as_mut(), r.as_mut()) } /// Produces the split parts of `self` using [`Self::via_no_dynamic_padding`]. #[deprecated(note = "use `Split::via_no_dynamic_padding` instead")] #[doc(hidden)] #[must_use = "has no side effects"] #[inline(always)] pub fn via_unaligned(self) -> (&'a mut T, &'a mut [T::Elem]) where T: Unaligned, { self.via_no_dynamic_padding() } /// Produces the split parts of `self` using a compile-time assertion to /// ensure that it is unconditionally sound to have concurrent references to /// both parts. /// /// When possible, you should prefer using [`Self::via_into_bytes`], which /// ensures this safety property through regular trait bounds. /// /// # Examples /// /// In the below example, a single byte of padding exists (on most /// platforms) between `Packet<[u16]>`'s `length` and `body` fields, /// precluding the use of [`Self::via_into_bytes`]. /// /// ``` /// use zerocopy::SplitAt; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, KnownLayout)] /// #[repr(C)] /// struct Packet { /// length: u8, /// body: B, /// } /// /// let packet: &mut Packet<[u16]> = &mut Packet { length: 4, body: [1u16, 2, 3, 4, 5, 6, 7, 8, 9] }; /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// // Every body length requires no trailing padding. /// if let Some(split) = packet.split_at_mut(packet.length as usize) { /// let (packet, rest) = split.via_no_dynamic_padding(); /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// rest.fill(0); /// } else { /// unreachable!("The packet's length field is within its body"); /// } /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]); /// ``` /// /// # Compile-Time Assertions /// /// This method rejects at compile-time any type that admits dynamic /// trailing padding. In the below example, some `split_at` indices would /// produce left parts whose trailing padding overlaps with the right part: /// /// ```compile_fail,E0080 /// use zerocopy::SplitAt; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, KnownLayout)] /// #[repr(C, align(2))] /// struct Packet { /// length: u16, /// body: B, /// } /// /// let packet: &mut Packet<[u8]> = &mut Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] }; /// /// if let Some(split) = packet.split_at_mut(packet.length as usize) { /// let _ = split.via_no_dynamic_padding(); // ⚠ Compile Error! /// } else { /// unreachable!("The packet's length field is within its body"); /// } /// ``` /// /// If you need to split such types, use [`Self::via_runtime_check`]. /// /// # Code Generation /// /// See [`Split::via_immutable`](#method.split_via_immutable.codegen). #[must_use = "has no side effects"] #[inline(always)] pub fn via_no_dynamic_padding(self) -> (&'a mut T, &'a mut [T::Elem]) { let (l, r) = self.into_ptr().via_no_dynamic_padding(); (l.as_mut(), r.as_mut()) } /// Produces the split parts of `self`, using a dynamic check to ensure that /// it is sound to have concurrent references to both parts. You should /// prefer using [`Self::via_into_bytes`] or /// [`Self::via_no_dynamic_padding`], which perform no runtime check. /// /// # Examples /// /// ``` /// use zerocopy::SplitAt; /// # use zerocopy_derive::*; /// /// #[derive(SplitAt, KnownLayout)] /// #[repr(C, align(2))] /// struct Packet { /// length: u16, /// body: B, /// } /// /// let packet: &mut Packet<[u8]> = &mut Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] }; /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// // The two-byte prefix and four-byte body need no trailing padding. /// if let Some(split) = packet.split_at_mut(packet.length as usize) { /// if let Ok((packet, rest)) = split.via_runtime_check() { /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4]); /// assert_eq!(rest, [5, 6, 7, 8, 9]); /// rest.fill(0); /// } else { /// unreachable!("A four-byte body requires no trailing padding"); /// } /// } else { /// unreachable!("The packet's length field is within its body"); /// } /// /// assert_eq!(packet.length, 4); /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]); /// ``` /// /// # Code Generation /// /// See [`Split::via_runtime_check`](#method.split_via_runtime_check.codegen). #[must_use = "has no side effects"] #[inline(always)] pub fn via_runtime_check(self) -> Result<(&'a mut T, &'a mut [T::Elem]), Self> { match self.into_ptr().via_runtime_check() { Ok((l, r)) => Ok((l.as_mut(), r.as_mut())), Err(s) => Err(s.into_mut()), } } /// Unsafely produces the split parts of `self`. /// /// # Safety /// /// The trailing padding bytes of the left portion must not overlap the /// right portion. For some dynamically sized types, the padding that /// appears after the trailing slice field [is a dynamic function of the /// trailing slice length](KnownLayout#slice-dst-layout). Thus, for some /// types, this condition is dependent on the length of the left portion. /// /// # Code Generation /// /// See [`Split::via_unchecked`](#method.split_via_unchecked.codegen). #[must_use = "has no side effects"] #[inline(always)] pub unsafe fn via_unchecked(self) -> (&'a mut T, &'a mut [T::Elem]) { // SAFETY: The aliasing of `self.into_ptr()` is `Exclusive`, and the // caller has promised that the left and right portions of `self` split // at `l_len` do not overlap. let (l, r) = unsafe { self.into_ptr().via_unchecked() }; (l.as_mut(), r.as_mut()) } } impl<'a, T, I> Split> where T: ?Sized + SplitAt, I: Invariants, { fn into_ref(self) -> Split<&'a T> where I: Invariants, { // SAFETY: `self.source.as_ref()` points to exactly the same referent as // `self.source` and thus maintains the invariants of `self` with // respect to `l_len`. unsafe { Split::new(self.source.as_ref(), self.l_len) } } fn into_mut(self) -> Split<&'a mut T> where I: Invariants, { // SAFETY: `self.source.as_mut()` points to exactly the same referent as // `self.source` and thus maintains the invariants of `self` with // respect to `l_len`. unsafe { Split::new(self.source.unify_invariants().as_mut(), self.l_len) } } /// Produces the length of `self`'s left part. #[inline(always)] fn l_len(&self) -> MetadataOf { // SAFETY: By invariant on `Split`, `self.l_len` is not greater than the // length of `self.source`. unsafe { MetadataOf::::new_unchecked(self.l_len) } } /// Produces the split parts of `self`, using [`Immutable`] to ensure that /// it is sound to have concurrent references to both parts. #[inline(always)] fn via_immutable(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>) where T: Immutable, I: Invariants, { // SAFETY: `Aliasing = Shared` and `T: Immutable`. unsafe { self.via_unchecked() } } /// Produces the split parts of `self`, using [`IntoBytes`] to ensure that /// it is sound to have concurrent references to both parts. #[inline(always)] fn via_into_bytes(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>) where T: IntoBytes, { // SAFETY: By `T: IntoBytes`, `T` has no padding for any length. // Consequently, `T` can be split into non-overlapping parts at any // index. unsafe { self.via_unchecked() } } /// Produces the split parts of `self` after the conservative layout /// predicate establishes that `T` never requires dynamic trailing padding. #[inline(always)] fn via_no_dynamic_padding(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>) { static_assert!( T: ?Sized + KnownLayout => !T::LAYOUT.requires_dynamic_padding(), "`Split::via_no_dynamic_padding` cannot be used with a type whose layout may require dynamic trailing padding; use `Split::via_runtime_check` instead" ); // SAFETY: The assertion establishes that `requires_dynamic_padding()` // is false. By that method's guarantee, every valid metadata—including // `self.l_len()`, which is valid by `Split`'s invariant—describes an // object with no trailing padding. By the postcondition of // `PtrInner::split_at_unchecked`, the two returned referents are then // contiguous and non-overlapping. Since the source already conforms to // `I::Aliasing`, splitting it into disjoint byte ranges preserves that // invariant: under `Exclusive`, neither result aliases or accesses the // other's bytes; under `Shared`, interior mutation through one result // cannot mutate bytes pointed to by the other. This satisfies Rust's // reference aliasing rules [1]. // // [1] Per https://doc.rust-lang.org/1.93.1/std/ptr/index.html#pointer-to-reference-conversion: // // When creating a mutable reference, then while this reference // exists, the memory it points to must not get accessed (read or // written) through any other pointer or reference not derived from // this reference. // // When creating a shared reference, then while this reference // exists, the memory it points to must not get mutated (except inside // `UnsafeCell`). unsafe { self.via_unchecked() } } /// Produces the split parts of `self`, using a dynamic check to ensure that /// it is sound to have concurrent references to both parts. You should /// prefer using [`Self::via_immutable`], [`Self::via_into_bytes`], or /// [`Self::via_no_dynamic_padding`], which perform no runtime check. #[inline(always)] fn via_runtime_check(self) -> Result<(Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>), Self> { let l_len = self.l_len(); // `l_len` witnesses an object size at most `isize::MAX`, and // `KnownLayout::LAYOUT` guarantees that it contains the trailing // slice. Thus `padding_for_elems` returns the exact trailing padding. let trailing_padding = crate::trailing_slice_layout::().padding_for_elems(l_len.get()); // FIXME(#1290): Once we require `KnownLayout` on all fields, add an // `IS_IMMUTABLE` associated const, and add `T::IS_IMMUTABLE ||` to the // below check. if trailing_padding == 0 { // SAFETY: As established above, `trailing_padding` is the exact // padding after the left part's trailing slice. If it is zero, // the left and right parts are strictly non-overlapping. Ok(unsafe { self.via_unchecked() }) } else { Err(self) } } /// Unsafely produces the split parts of `self`. /// /// # Safety /// /// The caller promises that if `I::Aliasing` is [`Exclusive`] or `T` /// permits interior mutation, then the left part has no trailing padding. #[inline(always)] unsafe fn via_unchecked(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>) { let l_len = self.l_len(); let inner = self.source.as_inner(); // SAFETY: By invariant on `Self::l_len`, `l_len` is not greater than // the length of `inner`'s trailing slice. let (left, right) = unsafe { inner.split_at_unchecked(l_len) }; // Lemma 0: `left` and `right` conform to the aliasing invariant // `I::Aliasing`. Proof: If `I::Aliasing` is `Exclusive` or `T` permits // interior mutation, the caller promises that the left part has no // trailing padding. Consequently, by the postcondition of // `PtrInner::split_at_unchecked`, `left` and `right` do not overlap. // If `I::Aliasing` is shared and `T` forbids interior mutation, then // overlap between their referents is permissible. // SAFETY: // 0. `left` conforms to the aliasing invariant of `I::Aliasing`, by Lemma 0. // 1. `left` conforms to the alignment invariant of `I::Alignment, because // the referents of `left` and `Self` have the same address and type // (and, thus, alignment requirement). // 2. `left` conforms to the validity invariant of `I::Validity`, neither // the type nor bytes of `left`'s referent have been changed. let left = unsafe { Ptr::from_inner(left) }; // SAFETY: // 0. `right` conforms to the aliasing invariant of `I::Aliasing`, by Lemma // 0. // 1. `right` conforms to the alignment invariant of `I::Alignment`: // if the source is aligned, `T: SplitAt` guarantees that its // trailing slice's address is aligned for `T::Elem`. Advancing by // whole elements preserves that alignment. // 2. `right` conforms to the validity invariant of `I::Validity`, // because `T: SplitAt` guarantees that `T::Elem` has the size and // bit validity of the original trailing element. It also guarantees // compatible shared and mutable access, so writes through `right` // preserve the original elements' validity and interior mutation // respects the original `UnsafeCell` coverage. The left part // cannot invalidate `right`: when the references are exclusive or // permit interior mutation, the caller guarantees no trailing // padding and hence no overlap. let right = unsafe { Ptr::from_inner(right) }; (left, right) } } #[cfg(test)] mod tests { use core::{cell::Cell, mem::ManuallyDrop}; use crate::{Immutable, KnownLayout, SplitAt, Unalign, Unaligned}; #[derive(KnownLayout, SplitAt, Immutable, Unaligned)] #[repr(C, packed)] struct Packed { prefix: u8, tail: ManuallyDrop, } #[derive(KnownLayout, SplitAt, Immutable)] #[repr(C, packed(2))] struct Packed2 { prefix: u8, tail: ManuallyDrop, } #[derive(KnownLayout, SplitAt, Immutable)] #[repr(C)] struct Inner { prefix: u32, tail: T, } #[test] fn test_split_at_packed() { use crate::{FromBytes, IntoBytes}; #[derive(FromBytes, IntoBytes, KnownLayout, SplitAt, Immutable, Unaligned)] #[repr(C, packed)] struct Packet { prefix: u8, tail: [u16], } // The tail starts at an odd address, exercising unaligned reads and // writes on targets where `u16` has alignment 2. #[repr(align(8))] struct Bytes([u8; 9]); for i in 0..=4 { let mut bytes = Bytes([1; 9]); let packet = Packet::ref_from_bytes(&bytes.0).unwrap(); let (left, right): (&Packet, &[Unalign]) = packet.split_at(i).unwrap().via_immutable(); assert_eq!(core::mem::size_of_val(left), 1 + 2 * i); assert_eq!(right.len(), 4 - i); assert!(right.iter().all(|elem| elem.get() == 0x0101)); assert!(left.split_at(i).is_some()); assert!(left.split_at(i + 1).is_none()); assert!(packet.split_at(5).is_none()); let (_, right) = packet.split_at(i).unwrap().via_into_bytes(); assert_eq!(right.len(), 4 - i); let (_, right) = packet.split_at(i).unwrap().via_no_dynamic_padding(); assert_eq!(right.len(), 4 - i); let packet = Packet::mut_from_bytes(&mut bytes.0).unwrap(); let (left, right): (&mut Packet, &mut [Unalign]) = packet.split_at_mut(i).unwrap().via_runtime_check().ok().unwrap(); left.prefix = 2; for elem in right { *elem = Unalign::new(0x0202); } assert_eq!(bytes.0[0], 2); assert!(bytes.0[1..1 + 2 * i].iter().all(|&byte| byte == 1)); assert!(bytes.0[1 + 2 * i..].iter().all(|&byte| byte == 2)); } } #[test] fn test_split_at_nested_packed() { // A packing factor greater than 1 still exposes `Unalign` elements, // including when the factor is below the original element alignment. let mut words = Packed2 { prefix: 0, tail: ManuallyDrop::new([1u32, 2, 3]) }; let dst: &mut Packed2<[u32]> = &mut words; let (left, right): (&mut _, &mut [Unalign]) = dst.split_at_mut(1).unwrap().via_no_dynamic_padding(); left.prefix = 4; right[0] = Unalign::new(5); let dst: &Packed2<[u32]> = &words; let (left, right) = dst.split_at(0).unwrap().via_immutable(); assert_eq!(left.prefix, 4); assert_eq!(right[0].get(), 1); assert_eq!(right[1].get(), 5); assert_eq!(right[2].get(), 3); let mut packet = Inner { prefix: 0, tail: Packed2 { prefix: 1, tail: ManuallyDrop::new(Packed { prefix: 2, tail: ManuallyDrop::new([3u16, 4, 5]), }), }, }; let dst: &Inner>> = &packet; let (left, right): (&_, &[Unalign>]) = dst.split_at(1).unwrap().via_immutable(); assert_eq!(left.prefix, 0); assert_eq!(right.len(), 2); assert_eq!(right[0].get().get(), 4); assert_eq!(right[1].get().get(), 5); // Split the packed field separately: the unpacked outer struct's // padding does not constrain this split. let dst: &mut Packed2> = &mut packet.tail; let (left, right): (&mut _, &mut [Unalign>]) = dst.split_at_mut(1).unwrap().via_no_dynamic_padding(); left.prefix = 6; right[0] = Unalign::new(Unalign::new(7)); let dst: &Inner>> = &packet; let (_, right) = dst.split_at(0).unwrap().via_immutable(); assert_eq!(right[0].get().get(), 3); assert_eq!(right[1].get().get(), 7); assert_eq!(right[2].get().get(), 5); } #[test] fn test_split_at_packed_preserves_inner_padding() { for i in 0..=4 { let mut packet = Packed { prefix: 1, tail: ManuallyDrop::new(Inner { prefix: 0, tail: [2u8, 3, 4, 5] }), }; let dst: &Packed> = &packet; let (left, right): (&_, &[Unalign]) = dst.split_at(i).unwrap().via_immutable(); assert_eq!(right.len(), 4 - i); assert_eq!(right.first().map(Unalign::get), [2, 3, 4, 5].get(i).copied()); // The inner `u32` prefix makes the tail start at byte 5 of the // packed wrapper. Its size may still include inner padding. let has_padding = core::mem::size_of_val(left) != 5 + i; assert_eq!(dst.split_at(i).unwrap().via_runtime_check().is_err(), has_padding); let dst: &mut Packed> = &mut packet; let split = dst.split_at_mut(i).unwrap().via_runtime_check(); assert_eq!(split.is_err(), has_padding); if let Ok((left, right)) = split { left.prefix = 6; for elem in right { *elem = Unalign::new(7); } let dst: &Packed> = &packet; let (left, right) = dst.split_at(i).unwrap().via_immutable(); assert_eq!(left.prefix, 6); assert!(right.iter().all(|elem| elem.get() == 7)); } } } #[test] fn test_split_at_packed_interior_mutation_and_zsts() { let packet = Packed { prefix: 0, tail: ManuallyDrop::new([Cell::new(1u8), Cell::new(2), Cell::new(3)]), }; let dst: &Packed<[Cell]> = &packet; let (left, right): (&_, &[Unalign>]) = dst.split_at(1).unwrap().via_no_dynamic_padding(); left.tail[0].set(4); right[0].try_deref().unwrap().set(5); assert_eq!(dst.tail[0].get(), 4); assert_eq!(dst.tail[1].get(), 5); assert_eq!(dst.tail[2].get(), 3); let mut packet = Packed { prefix: 0, tail: ManuallyDrop::new([(); 3]) }; for i in 0..=3 { let dst: &mut Packed<[()]> = &mut packet; let (left, right): (&mut _, &mut [Unalign<()>]) = dst.split_at_mut(i).unwrap().via_no_dynamic_padding(); assert_eq!(left.tail.len(), i); assert_eq!(right.len(), 3 - i); assert_eq!(core::mem::size_of_val(left), 1); } } #[cfg(feature = "derive")] #[test] fn test_split_at() { use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt}; #[derive(FromBytes, KnownLayout, SplitAt, IntoBytes, Immutable, Debug)] #[repr(C)] struct SliceDst { prefix: [u8; OFFSET], trailing: [u8], } #[allow(clippy::as_conversions)] fn test_split_at() { // Test `split_at` let n: usize = BUFFER_SIZE - OFFSET; let arr = [1; BUFFER_SIZE]; let dst = SliceDst::::ref_from_bytes(&arr[..]).unwrap(); for i in 0..=n { let (l, r) = dst.split_at(i).unwrap().via_runtime_check().unwrap(); let l_sum: u8 = l.trailing.iter().sum(); let r_sum: u8 = r.iter().sum(); assert_eq!(l_sum, i as u8); assert_eq!(r_sum, (n - i) as u8); assert_eq!(l_sum + r_sum, n as u8); } // Test `split_at_mut` let n: usize = BUFFER_SIZE - OFFSET; let mut arr = [1; BUFFER_SIZE]; let dst = SliceDst::::mut_from_bytes(&mut arr[..]).unwrap(); for i in 0..=n { let (l, r) = dst.split_at_mut(i).unwrap().via_runtime_check().unwrap(); let l_sum: u8 = l.trailing.iter().sum(); let r_sum: u8 = r.iter().sum(); assert_eq!(l_sum, i as u8); assert_eq!(r_sum, (n - i) as u8); assert_eq!(l_sum + r_sum, n as u8); } } test_split_at::<0, 16>(); test_split_at::<1, 17>(); test_split_at::<2, 18>(); } #[cfg(feature = "derive")] #[test] #[allow(clippy::as_conversions)] fn test_split_at_overlapping() { use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt}; #[derive(FromBytes, KnownLayout, SplitAt, Immutable)] #[repr(C, align(2))] struct SliceDst { prefix: u8, trailing: [u8], } assert!(SliceDst::LAYOUT.requires_dynamic_padding()); const N: usize = 16; let arr = [1u16; N]; let dst = SliceDst::ref_from_bytes(arr.as_bytes()).unwrap(); for i in 0..N { let split = dst.split_at(i).unwrap().via_runtime_check(); if i % 2 == 1 { assert!(split.is_ok()); } else { assert!(split.is_err()); } } } #[test] fn test_split_at_unchecked() { use crate::SplitAt; let mut arr = [1, 2, 3, 4]; let slice = &arr[..]; // SAFETY: 2 <= arr.len() (4) let split = unsafe { SplitAt::split_at_unchecked(slice, 2) }; // SAFETY: SplitAt::split_at_unchecked guarantees that the split is valid. let (l, r) = unsafe { split.via_unchecked() }; assert_eq!(l, &[1, 2]); assert_eq!(r, &[3, 4]); let slice_mut = &mut arr[..]; // SAFETY: 2 <= arr.len() (4) let split = unsafe { SplitAt::split_at_mut_unchecked(slice_mut, 2) }; // SAFETY: SplitAt::split_at_mut_unchecked guarantees that the split is valid. let (l, r) = unsafe { split.via_unchecked() }; assert_eq!(l, &mut [1, 2]); assert_eq!(r, &mut [3, 4]); } #[test] fn test_split_at_via_methods() { use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt}; #[derive(FromBytes, KnownLayout, SplitAt, IntoBytes, Immutable, Debug)] #[repr(C)] struct Packet { length: u8, body: [u8], } let arr = [1, 2, 3, 4]; let packet = Packet::ref_from_bytes(&arr[..]).unwrap(); let split1 = packet.split_at(2).unwrap(); let (l, r) = split1.via_immutable(); assert_eq!(l.length, 1); assert_eq!(r, &[4]); let split2 = packet.split_at(2).unwrap(); let (l, r) = split2.via_into_bytes(); assert_eq!(l.length, 1); assert_eq!(r, &[4]); } #[test] #[allow(deprecated)] fn test_split_at_via_unaligned() { use crate::{Immutable, KnownLayout, Split, SplitAt, Unaligned}; fn via_unaligned<'a, T>(split: Split<&'a T>) -> (&'a T, &'a [T::Elem]) where T: ?Sized + SplitAt + Unaligned, { split.via_unaligned() } fn via_unaligned_mut<'a, T>(split: Split<&'a mut T>) -> (&'a mut T, &'a mut [T::Elem]) where T: ?Sized + SplitAt + Unaligned, { split.via_unaligned() } #[derive(KnownLayout, SplitAt, Immutable, Unaligned)] #[repr(C)] struct Packet { prefix: [u8; 2], body: B, } // Exercise generic callers using the original `T: Unaligned` bound. assert!(!Packet::<[[u8; 2]]>::LAYOUT.requires_dynamic_padding()); let packet = Packet { prefix: [0, 1], body: [[2, 3], [4, 5], [6, 7]] }; let packet: &Packet<[[u8; 2]]> = &packet; let split = packet.split_at(2).unwrap(); let (l, r) = via_unaligned(split); assert_eq!(l.body, [[2, 3], [4, 5]]); assert_eq!(r, &[[6, 7]]); let mut packet = Packet { prefix: [0, 1], body: [[2, 3], [4, 5], [6, 7]] }; { let packet: &mut Packet<[[u8; 2]]> = &mut packet; let split = packet.split_at_mut(2).unwrap(); let (l, r) = via_unaligned_mut(split); l.body[0] = [8, 9]; r[0] = [10, 11]; } assert_eq!(packet.body, [[8, 9], [4, 5], [10, 11]]); } #[test] fn test_split_at_via_no_dynamic_padding() { use core::cell::Cell; use crate::SplitAt; // `u16` does not implement `Unaligned`, but `[u16]` never requires // dynamic trailing padding. let words = [1u16, 2, 3, 4]; let split = SplitAt::split_at(&words[..], 2).unwrap(); let (left, right) = split.via_no_dynamic_padding(); assert_eq!(left, [1, 2]); assert_eq!(right, [3, 4]); let mut words = [1u16, 2, 3, 4]; let split = SplitAt::split_at_mut(&mut words[..], 2).unwrap(); let (left, right) = split.via_no_dynamic_padding(); left[0] = 5; right[0] = 6; assert_eq!(words, [5, 2, 6, 4]); // Exercise the safety-sensitive shared route with interior mutation. let cells = [Cell::new(1u16), Cell::new(2), Cell::new(3)]; let split = SplitAt::split_at(&cells[..], 2).unwrap(); let (left, right) = split.via_no_dynamic_padding(); left[0].set(4); right[0].set(5); assert_eq!([cells[0].get(), cells[1].get(), cells[2].get()], [4, 2, 5]); } }