In Rust, a slice is a reference to a contiguous sequence of elements in a collection, rather than the whole collection. Slices allow you to refer to a part of a collection without taking ownership of the entire collection. This makes them very efficient for passing data around, as they avoid unnecessary copying.
Key characteristics of slices:
1. References: Slices are always references. This means they borrow data and do not take ownership. They come in two main forms:
* `&[T]`: An immutable reference to a slice of type `T`. This is often called a 'shared slice'.
* `&mut [T]`: A mutable reference to a slice of type `T`. This allows modification of the elements within the slice.
2. Pointer and Length: Internally, a slice is a 'fat pointer' — it's a two-word object consisting of a pointer to the starting element of the sequence and the length of the sequence.
3. No Compile-Time Fixed Size: Unlike arrays (`[T; N]`), slices do not have a fixed size known at compile time. Their length is determined at runtime, making them flexible for working with different parts of collections.
4. Common Use Cases: Slices are extensively used with:
* Strings: The `&str` type is a string slice, representing a view into a `String` or a string literal. It's the most common way to pass string data to functions.
* Vectors and Arrays: `&[T]` can be used to refer to a portion of a `Vec<T>` or a fixed-size array `[T; N]`.
5. Safety and Efficiency: Slices are a core part of Rust's safety and performance guarantees:
* No Ownership Transfer: By borrowing, slices avoid the overhead of copying data, making operations very fast.
* Dangling Pointers Prevented: The Rust compiler ensures that a slice never outlives the data it refers to, preventing common memory safety bugs like use-after-free.
6. Creation: Slices are typically created using a range syntax `[starting_index..ending_index]` on owned data structures like `String` or `Vec<T>`. If the starting index is omitted, it defaults to 0. If the ending index is omitted, it defaults to the length of the collection.
In essence, slices provide a flexible, safe, and efficient way to work with parts of collections without incurring the cost or complexity of ownership transfer.
Example Code
```rust
fn main() {
// --- String Slices (\u0026str) ---
// 1. From a String literal
let s_literal: &str = "Hello, world!"; // s_literal is already a string slice
println!("String literal: {}", s_literal);
// 2. From an owned String
let s_owned = String::from("Rust programming is fun");
// Create slices from s_owned
let word1 = &s_owned[0..4]; // "Rust"
let word2 = &s_owned[5..16]; // "programming"
let rest_of_string = &s_owned[17..]; // "is fun" (ends at the end of the string)
let full_slice = &s_owned[..]; // "Rust programming is fun" (entire string as a slice)
println!("From owned String: ");
println!(" Word 1: {}", word1);
println!(" Word 2: {}", word2);
println!(" Rest: {}", rest_of_string);
println!(" Full: {}", full_slice);
// --- Array/Vector Slices (\u0026[T]) ---
// 1. From a fixed-size array
let a: [i32; 5] = [1, 2, 3, 4, 5];
let slice_a: &[i32] = &a[1..4]; // Slice containing [2, 3, 4]
println!("\nFrom array: {:?}", slice_a);
// 2. From a Vec<T>
let v: Vec<f64> = vec![10.0, 20.0, 30.0, 40.0, 50.0];
let slice_v_immutable: &[f64] = &v[2..]; // Slice containing [30.0, 40.0, 50.0]
println!("From Vec (immutable): {:?}", slice_v_immutable);
// --- Mutable Slices (\u0026mut [T]) ---
let mut numbers: Vec<i32> = vec![100, 200, 300, 400, 500];
// Create a mutable slice
let mutable_slice: &mut [i32] = &mut numbers[1..4]; // References [200, 300, 400]
println!("\nOriginal mutable slice: {:?}", mutable_slice);
// Modify elements through the mutable slice
mutable_slice[0] = 201; // changes numbers[1]
mutable_slice[1] = 301; // changes numbers[2]
println!("Modified mutable slice: {:?}", mutable_slice);
println!("Original Vec after modification: {:?}", numbers); // Shows [100, 201, 301, 400, 500]
// --- Functions that take slices ---
// A function that works on any slice of i32
fn print_slice(slice: &[i32]) {
println!("Function received slice: {:?}, length: {}", slice, slice.len());
}
println!("\nPassing slices to a function:");
print_slice(&a[..]); // Pass a slice of the entire array
print_slice(&numbers[..2]); // Pass a slice of the first two elements of the vector
// A function that can modify elements in a mutable slice
fn double_elements(slice: &mut [i32]) {
for element in slice.iter_mut() {
*element *= 2;
}
}
let mut scores = vec![1, 2, 3, 4, 5];
println!("Scores before doubling: {:?}", scores);
double_elements(&mut scores[1..4]); // Double elements from index 1 to 3
println!("Scores after doubling: {:?}", scores);
}
```








The Slice Type