Slices
Slice References
Because bare slice types ([T] and str) are Dynamically Sized Types (DSTs) whose byte length is not known at compile time, slices can never be held directly by value on the stack. Instead, they must always be accessed through a pointer or slice reference (&[T], &mut [T], &str, or &mut str).
Why Bare [T] and str Require Indirection (!Sized)
In Rust, local variables and function parameters must implement the Sized marker trait so the compiler knows how many bytes to reserve in the stack frame.
[T; N]isSized(size_of::<T>() * Nbytes).[T]andstrare!Sized(unsized)—their length depends on runtime data.- Placing
&or&mutin front of[T]orstrcreates aSized16-byte fat-pointer reference (&[T]or&str) whose size on the stack is always known at compile time (2 * size_of::<usize>()):
// ❌ does not compile
fn accept_unsized(s: [i32]) {}
fn main() {}
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
--> src/main.rs:1:19
|
1 | fn accept_unsized(s: [i32]) {}
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[i32]`
help: function arguments must have a statically known size, borrowed types always have a known size
|
1 | fn accept_unsized(s: &[i32]) {}
| +
error: aborting due to 1 previous error
Comparing Reference Types That Point to Sequences
| Reference / Pointer Type | Stack Size (64-bit) | What It Points To | Flexibility |
|---|---|---|---|
&[T] (Shared slice ref) |
16 bytes (ptr + len) |
Any contiguous [T] in stack, heap, or static memory |
Most flexible read-only view |
&mut [T] (Mutable slice ref) |
16 bytes (ptr + len) |
Any mutable contiguous [T] |
Most flexible in-place read/write view |
&[T; N] (Array ref) |
8 bytes (ptr only) |
Only an array of exact compile-time length N |
Coerces automatically to &[T] |
&Vec<T> (Vector ref) |
8 bytes (ptr to Vec header) |
Double indirection (&Vec -> Vec -> heap [T]) |
Coerces automatically to &[T]; avoid in parameters! |
Box<[T]> (Boxed slice) |
16 bytes (ptr + len) |
Owns a fixed-length heap allocation of [T] (no extra cap word) |
Saves 8 bytes over Vec<T> when length will never grow |
Why `&[T]` Is Faster and More Direct Than `&Vec<T>`:
&Vec<T> (Double Indirection): r: &Vec<T> ──> Vec Header (ptr, len, cap) ──> Heap Elements [T]
&[T] (Direct Indirection): s: &[T] (ptr, len) ─────────────────────────> Heap Elements [T]
use std::mem::size_of;
fn sum_slice(data: &[i32]) -> i32 {
data.iter().sum()
}
fn main() {
let vec_data: Vec<i32> = vec![10, 20, 30];
// Convert `Vec<i32>` into an owned 16-byte `Box<[i32]>` once it no longer needs to grow:
let boxed_slice: Box<[i32]> = vec_data.into_boxed_slice();
println!("Sum via &[i32]: {}", sum_slice(&boxed_slice));
println!("size_of::<Vec<i32>>() = {} bytes", size_of::<Vec<i32>>());
println!("size_of::<Box<[i32]>>() = {} bytes", size_of::<Box<[i32]>>());
}
Sum via &[i32]: 60
size_of::<Vec<i32>>() = 24 bytes
size_of::<Box<[i32]>>() = 16 bytes
Explicit Slice Reference Conversion Methods
While &arr and &vec coerce to &[T] automatically when a target type : &[T] is present, you can also obtain slice references explicitly when type inference needs a concrete method:
collection.as_slice()(or&collection[..]) -> returns&[T]collection.as_mut_slice()(or&mut collection[..]) -> returns&mut [T]string.as_str()(or&string[..]) -> returns&strstd::slice::from_ref(&single_val)-> safely views a single&Tas a 1-element slice&[T]of length1without copying!