Arrays and Slices
Arrays ([T; N]) and slices ([T], accessed via &[T] or &mut [T]) are Rust's foundational contiguous sequence types. Every element i sits at exact byte offset i * size_of::<T>() from the base address, giving $O(1)$ random access, zero per-element pointer overhead, and maximum CPU cache locality.
Fixed-Size Array `[u32; 4]` (16 Bytes Inline on Stack or inside Struct):
+----------+----------+----------+----------+
| 10 | 20 | 30 | 40 |
+----------+----------+----------+----------+
Offset 0 Offset 4 Offset 8 Offset 12
Borrowed Slice `&[u32]` (16-Byte Fat Pointer referencing any contiguous `[u32]`):
+---------------------+---------------------+
| data_ptr (*const T) | len: usize (e.g. 2) |
+---------------------+---------------------+
|
+-----------> Points to `[20, 30]` sub-range above!
Comparing [T; N], &[T], and Box<[T]>
| Type | Size Known at Compile Time? | Where Elements Live | Stack Footprint (64-bit) | Can Length Change? |
|---|---|---|---|---|
[T; N] |
Yes (N is a const usize) |
Inline (stack or parent struct) | N * size_of::<T>() |
No |
&[T] / &mut [T] |
No (Unsized DST [T] behind ref) |
Borrowed from [T; N], Vec<T>, etc. |
16 bytes (ptr + len) |
Re-sliceable (s = &s[1..]) |
Box<[T]> |
No | Owned on the heap | 16 bytes (ptr + len, drops cap word of Vec<T>) |
No (saves 8 bytes vs Vec<T>) |
Splitting Mutable Slices Safely Without Aliasing (split_at_mut)
Because the borrow checker tracks borrows at the whole-slice variable level, writing &mut slice[0] and &mut slice[1] simultaneously is rejected as two mutable borrows of slice.
Standard library slice methods—such as split_at_mut(mid), split_first_mut(), chunks_exact_mut(k), and as_chunks_mut::<N>()—encapsulate the single internal unsafe pointer split and return disjoint mutable sub-slices that can be mutated concurrently across threads or passed to functions together:
use std::mem::size_of;
/// Performs an in-place partition step or parallel half-transform using `split_at_mut`.
pub fn mirror_add_halves(data: &mut [i32]) {
let mid = data.len() / 2;
// Splits `data` into two non-overlapping `&mut [i32]` slices safely!
let (left, right) = data.split_at_mut(mid);
for (l, r) in left.iter_mut().zip(right.iter_mut().rev()) {
let sum = *l + *r;
*l = sum;
*r = sum;
}
}
fn main() {
let mut values: [i32; 6] = [1, 2, 3, 10, 20, 30];
mirror_add_halves(&mut values);
assert_eq!(values, [31, 22, 13, 13, 22, 31]);
// Converting a finalized Vec<T> into Box<[T]> shrinks its header from 24B to 16B:
let boxed_slice: Box<[i32]> = Vec::from(values).into_boxed_slice();
assert_eq!(size_of::<Vec<i32>>(), 24);
assert_eq!(size_of::<Box<[i32]>>(), 16);
println!("Mirrored array: {values:?}, boxed slice len={}", boxed_slice.len());
}
Mirrored array: [31, 22, 13, 13, 22, 31], boxed slice len=6
High-Speed Algorithmic Operations Built into [T]
slice.binary_search(&target): $O(\log n)$ search on sorted slices returningOk(index)orErr(insertion_index).slice.partition_point(|x| pred(x)): $O(\log n)$ binary search for the exact boundary index where a monotonic predicate transitions fromtruetofalse.slice.select_nth_unstable(k): $O(n)$ Quickselect (Introselect) algorithm that finds the $k$-th smallest element and partitions the slice around it in linear time without sorting the entire slice!slice.rotate_left(k)/rotate_right(k): $O(n)$ in-place block rotation using $O(1)$ auxiliary memory.