What Is The Slices In Rust?

In Rust, when we create an array or a vector, we usually deal with the whole collection. But many times we don’t need the whole thing, we only want to work with some part. For this problem, Rust gives us slices.

A slice is like a window or subsection of an array/vector/string.

  • It doesn’t own the data, which means it doesn’t create a new copy.
  • It just borrows a portion of data from the original collection.
  • It’s a cafe because Rust checks that you don’t go outside the collection’s range.

They are typically created using a reference and a range of indices.

Creating Slices

Slices can be created from collections like arrays or vectors using slicing syntax (&collection[start..end]).

Example: Creating a Slice

fn main() {
let marks = [45, 56, 67, 78, 89, 90];

// Take a slice from index 2 to 5 (index 5 is not included)
let part = &marks[2..5];

println!("Selected part of marks: {:?}", part);
}

Output:

Selected part of marks: [67, 78, 89]

Explanation:

  • Here we have an array of marks with 6 elements.
  • &marks[2..5] means, start from index 2, which is the 3rd element and go up to index 5, but don’t include the element at index 5.
  • So we get 67, 78, and 89

Important points:

  • The slice does not create a new array.
  • It is just a borrowed view into the original array.

Types of Slices

  1. Immutable Slices (&[T]): We can only read the part of the collection
  2. Mutable Slices (&mut [T]): You can change the values inside that selected portion.

Mutable Slice Example

fn main() {
let mut scores = [12, 24, 36, 48, 60];

// Take a mutable slice from index 1 to 4
let slice_part = &mut scores[1..4];

// Update values inside the slice
slice_part[0] = 100; // changes scores[1]
slice_part[2] = 200; // changes scores[3]

println!("Array after changes: {:?}", scores);
}

Output:

Array after changes: [12, 100, 36, 200, 60]
  • The original array itself gets updated, because the slice is just a window into the same data, not a copy.

Slices and Functions In Rust

When we create a function, we don’t need to pass the entire array or vector; we only need a part of it.

If we pass the whole array, there can be ownership issues (which is one of Rust’s strict rules).

Example: Passing Slice to Function

fn main() {
let ages = [15, 18, 21, 25, 30];

// Pass a slice (from index 1 to 4) to the function
show_slice(&ages[1..4]);
}

// Function takes an immutable slice
fn show_slice(slice: &[i32]) {
println!("Inside function, printing slice elements:");
for (i, val) in slice.iter().enumerate() {
println!("Index {} -> {}", i, val);
}
}

Output:

Inside function, printing slice elements:
Index 0 -> 18
Index 1 -> 21
Index 2 -> 25
  • The function didn’t get ownership of the array. After the function call, the array is still fully accessible in main.

String Slices In Rust

A string slice allows you to work with a part of a string without creating a new copy. This is very efficient, especially when dealing with large strings, because Rust only creates a reference, or a “window,” into the original string rather than duplicating it.

String slices are written as &str, and you can use a range of indices to select the portion you want. One important thing to remember is that Rust strings are stored in UTF-8 encoding, so the indices in a slice refer to bytes rather than characters.

Example of String Slices

fn main() {
let message = String::from("Rust Programming");

// Take a slice of the first 4 characters
let first_part = &message[0..4];

// Take a slice of a middle word
let middle_part = &message[5..16];

println!("First part: {}", first_part); // Rust
println!("Middle part: {}", middle_part); // Programming
}

Output:

First part: Rust
Middle part: Programming

Explanation:

  • The message is a string “Rust Programming”.
  • &message[0..4] → slices the first 4 bytes → “Rust”
  • &message[5..16] → slices from byte 5 to 15 → “Programming”

Default Start and End Indices In Rust

when you create a slice, you don’t always need to specify both the start and end indices.

If you leave out the start index, Rust automatically assumes it is 0, which means the slice will begin from the first element.

Similarly, if you leave out the end index, Rust takes it as the length of the collection, meaning the slice will go all the way to the last element.

Example: Default Indices

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

// Slice from start to index 2 (0..3)
let first_part = &scores[..3];

// Slice from index 2 to the end
let last_part = &scores[2..];

println!("From start to index 2: {:?}", first_part);
println!("From index 2 to end: {:?}", last_part);
}

Output:

From start to index 2: [10, 20, 30]
From index 2 to end: [30, 40, 50]

Exercise: Find the Largest Number in a Slice

Task:

  1. Create an array of integers with at least 6 elements.
  2. Ask the user (or just define in code) to take a slice of 3 consecutive numbers from the array.
  3. Write a function that takes this slice and finds the largest number in it.
  4. Print the result.

Learn Other Topics About Rust Programming

Leave a Comment