Rust - Slices
Rust - Slices
Section titled “Rust - Slices”A slice is a view or reference into a contiguous block of memory owned by another data structure. Slices allow you to work with a portion of a collection like an array, a vector, or a String without copying the underlying data. This makes them efficient and powerful.
Key properties of slices:
- They borrow data; they don’t own it.
- Their size is determined at runtime.
- They are passed by reference to functions, promoting efficient, ownership-aware code.
String Slices (&str)
Section titled “String Slices (&str)”The most common type of slice is the string slice, written as &str. It’s a reference to a part of a String or a string literal.
Syntax
Section titled “Syntax”let collection = [1, 2, 3, 4, 5];let slice = &collection[start_index..end_index]; // `end_index` is exclusiveThe range start..end creates a slice that includes elements from start up to, but not including, end.
fn main() { let s = String::from("Hello, world!");
// Create slices using range syntax let hello = &s[0..5]; // or &s[..5] let world = &s[7..12]; // or &s[7..s.len()-1] if you're careful let full_slice = &s[..]; // A slice of the entire string
println!("Slice 1: {}", hello); println!("Slice 2: {}", world); println!("Full slice: {}", full_slice);}Output
Section titled “Output”Slice 1: HelloSlice 2: worldFull slice: Hello, world!Important: UTF-8 and Slicing
Section titled “Important: UTF-8 and Slicing”Warning: Rust strings are UTF-8 encoded. Some characters take more than one byte. If you try to create a string slice in the middle of a multi-byte character, your program will panic at runtime. Slicing must occur on valid character boundaries.
// This will PANIC!// The character '👋' is multiple bytes long.let wave = "👋";// let slice = &wave[0..1]; // Panics: `byte index 1 is not a char boundary`To safely work with characters, it’s better to iterate over them using .chars().
Slicing an Array
Section titled “Slicing an Array”Slices work just as well on arrays and vectors. The type of a slice of i32 values is &[i32].
fn process_slice(slice: &[i32]) { println!("Slice has {} elements", slice.len()); println!("First element: {}", slice[0]); println!("Full slice content: {:?}", slice);}
fn main() { let data = [10, 20, 30, 40, 50];
// Pass a slice of the middle three elements process_slice(&data[1..4]); // Borrows elements at index 1, 2, and 3}Output
Section titled “Output”Slice has 3 elementsFirst element: 20Full slice content: [20, 30, 40]Mutable Slices
Section titled “Mutable Slices”If you have a mutable collection, you can create a mutable slice using &mut. A mutable slice allows you to change the values of the elements it references.
fn modify_slice(slice: &mut [i32]) { println!("Original slice: {:?}", slice); slice[0] = 999; // Modify the first element of the slice println!("Modified slice: {:?}", slice);}
fn main() { let mut data = [10, 20, 30, 40, 50]; println!("Original array before: {:?}", data);
// Pass a mutable slice modify_slice(&mut data[1..4]);
println!("Original array after: {:?}", data);}Output
Section titled “Output”Original array before: [10, 20, 30, 40, 50]Original slice: [20, 30, 40]Modified slice: [999, 30, 40]Original array after: [10, 999, 30, 40, 50]Notice how modifying the slice directly changed the original data array. This is the power of borrowing in action.