Home Rust Tutorial: Slices in Rust Programming Language

Tutorial: Slices in Rust Programming Language

Slices in Rust are references to a contiguous segment of elements in a collection, such as an array or a string.

They allow you to work with a subset of data without copying it.

Slices are powerful and ensure safety by preventing out-of-bounds access.

What You’ll Learn

1. What Are Slices?

A slice in Rust is a dynamically-sized reference to part of a collection. It is created by borrowing a portion of a collection, and its type is &[T] for an array slice or &str for a string slice.

Example: Array Slices

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[1..4]; // Borrow elements 1 through 3 (exclusive of 4)
    println!("Slice: {:?}", slice); // Output: [2, 3, 4]
}

2. Working with Array Slices

You can create slices from arrays using a range.

Example: Basic Array Slices

fn main() {
    let arr = [10, 20, 30, 40, 50];
    
    let full_slice = &arr[..];       // Slice the entire array
    let part_slice = &arr[1..4];     // Slice from index 1 to 3
    
    println!("Full slice: {:?}", full_slice);
    println!("Part slice: {:?}", part_slice);
}

Example: Accessing Slice Elements

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[2..]; // Slice from index 2 to the end
    
    println!("First element: {}", slice[0]); // Accessing elements
    println!("Slice length: {}", slice.len());
}

3. String Slices

String slices (&str) are references to a part of a String or string literal.

Example: Slicing Strings

fn main() {
    let s = String::from("Hello, Rust!");
    
    let slice = &s[0..5]; // Slice the first 5 characters
    println!("Slice: {}", slice); // Output: Hello
}

Example: String Literals as Slices

String literals (“Hello”) are inherently string slices (&str).

fn main() {
    let literal: &str = "Rust";
    println!("String slice: {}", literal);
}

4. Slices and Iteration

Slices can be easily iterated using loops.

Example: Iterating Over an Array Slice

fn main() {
    let arr = [10, 20, 30, 40, 50];
    let slice = &arr[1..4];

    for &item in slice {
        println!("Item: {}", item);
    }
}

Example: Iterating Over a String Slice

fn main() {
    let s = "Rust";

    for c in s.chars() {
        println!("{}", c);
    }
}

5. Slices as Function Parameters

Slices are often used as function parameters to allow borrowing part of an array or string.

Example: Function with Array Slice Parameter

fn sum(slice: &[i32]) -> i32 {
    slice.iter().sum()
}

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[1..4]; // Borrow part of the array

    println!("Sum: {}", sum(slice)); // Output: 9
}

Example: Function with String Slice Parameter

fn print_slice(slice: &str) {
    println!("Slice: {}", slice);
}

fn main() {
    let s = String::from("Hello, Rust!");
    print_slice(&s[0..5]); // Pass a slice of the string
}

6. Mutable Slices

Mutable slices allow modifying the elements of the original collection.

Example: Modifying Elements

fn main() {
    let mut arr = [1, 2, 3, 4, 5];
    let slice = &mut arr[1..4]; // Mutable slice

    for elem in slice.iter_mut() {
        *elem *= 2; // Double each element
    }

    println!("Modified array: {:?}", arr); // Output: [1, 4, 6, 8, 5]
}

7. Slices with Ranges

Slices are commonly created using range syntax.

Example: Range Syntax

fn main() {
    let arr = [10, 20, 30, 40, 50];

    let slice1 = &arr[..];      // Entire array
    let slice2 = &arr[..3];     // From start to index 2
    let slice3 = &arr[2..];     // From index 2 to the end
    let slice4 = &arr[1..4];    // From index 1 to 3

    println!("slice1: {:?}", slice1);
    println!("slice2: {:?}", slice2);
    println!("slice3: {:?}", slice3);
    println!("slice4: {:?}", slice4);
}

8. Practical Examples

8.1 Finding the Maximum Value in a Slice

fn find_max(slice: &[i32]) -> i32 {
    *slice.iter().max().unwrap()
}

fn main() {
    let arr = [3, 7, 2, 8, 4];
    let slice = &arr[1..4]; // Borrow part of the array

    println!("Max value: {}", find_max(slice)); // Output: 8
}

8.2 Splitting a String

fn main() {
    let sentence = "Rust is amazing!";
    let words: Vec<&str> = sentence.split_whitespace().collect();

    for word in words {
        println!("{}", word);
    }
}

8.3 Reversing a Slice

fn reverse_slice(slice: &mut [i32]) {
    slice.reverse();
}

fn main() {
    let mut arr = [1, 2, 3, 4, 5];
    reverse_slice(&mut arr[1..4]); // Reverse part of the array

    println!("Reversed array: {:?}", arr); // Output: [1, 4, 3, 2, 5]
}

8.4 String Substring Search

fn contains_word(sentence: &str, word: &str) -> bool {
    sentence.contains(word)
}

fn main() {
    let text = "Rust is fast and safe!";
    println!("Contains 'fast': {}", contains_word(text, "fast")); // Output: true
}

9. Summary

Key Features

  • Slices are references to parts of arrays or strings.
  • Array slices have the type &[T].
  • String slices have the type &str.
  • Slices are used for borrowing data without copying.
  • They ensure safety by preventing out-of-bounds access.

Common Use Cases

  • Working with parts of arrays or strings.
  • Passing data to functions without ownership transfer.
  • Iterating over collections safely.

Rust slices are a fundamental feature for safe and efficient programming.

You may also like