Rust LogoThe Slice Type

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);
}
```