Slices in Rust
About This Module
This module introduces slices, a powerful feature in Rust that provides references to contiguous sub-sequences of collections. We'll explore how slices work with arrays and vectors, their memory representation, and how they interact with Rust's borrowing rules.
Prework
Prework Reading
Read the following sections from "The Rust Programming Language" book:
You might want to go back and review:
Pre-lecture Reflections
Before class, consider these questions:
- How do slices provide safe access to sub-sequences without copying data?
- What are the advantages of slices over passing entire arrays or vectors?
- How do borrowing rules apply to slices and prevent data races?
- When would you use slices instead of iterators for processing sub-sequences?
- What are the memory efficiency benefits of slices compared to copying data?
Learning Objectives
By the end of this module, you should be able to:
- Create and use immutable and mutable slices from arrays and vectors
- Understand slice syntax and indexing operations
- Apply borrowing rules correctly when working with slices
- Analyze the memory representation of slices
- Use slices for efficient sub-sequence processing without data copying
- Design functions that work with slice parameters for flexibility
Slices (ยง4.3)
Slice = reference to a contiguous sub-sequence of elements in a collection
Slices of an array:
- array of type
[T, _], e.g. datatype and length - slice of type
&[T](immutable) or&mut [T](mutable)
fn main() { let arr: [i32; 5] = [0,1,2,3,4]; println!("arr: {:?}", arr); // immutable slice of an array let slice: &[i32] = &arr[1..3]; println!("slice: {:?}",slice); println!("slice[0]: {}", slice[0]); }
The slice slice is a reference to the array arr from index 1 to 3 and hence is borrowed from arr.
Immutable slices
Note:
- The slice is a reference to the array, which by default is immutable.
- Even if the source array is mutable, the slice is immutable.
fn main() { let mut arr: [i32; 5] = [0,1,2,3,4]; println!("arr: {:?}", arr); // immutable slice of an array let slice: &[i32] = &arr[1..3]; println!("slice: {:?}",slice); println!("slice[0]: {}", slice[0]); slice[0] = 100; // ERROR! Cannot modify an immutable slice println!("slice: {:?}", slice); println!("slice[0]: {}", slice[0]); }
Mutable slices
We can create a mutable slice from a mutable array which borrows from arr mutably.
fn main(){ // mutable slice of an array let mut arr = [0,1,2,3,4]; println!("arr: {:?}", arr); let mut slice = &mut arr[2..4]; println!("slice: {:?}",slice); // ERROR: Cannot modify the source array after a borrow //arr[0] = 10; //println!("arr: {:?}", arr); println!("\nLet's modify the slice[0]"); slice[0] = slice[0] * slice[0]; println!("slice[0]: {}", slice[0]); println!("slice: {:?}", slice); println!("arr: {:?}", arr); }
What about this?
What's happening here?!?!?
Why are we able to modify the array after the slice is created?
fn main() { let mut arr: [i32; 5] = [0,1,2,3,4]; println!("arr: {:?}", arr); // immutable slice of an array let slice: &[i32] = &arr[1..3]; println!("slice: {:?}",slice); println!("slice[0]: {}", slice[0]); arr[0] = 10; // OK! We can modify the array println!("arr: {:?}", arr); // What happens if you uncomment this line? //println!("slice: {:?}", slice); }
Answer:
Slices with Vectors
Work for vectors too!
fn main() { let mut v = vec![0,1,2,3,4]; { let slice = &v[1..3]; println!("{:?}",slice); } { let mut slice = &mut v[1..3]; // iterating over slices works as well for x in slice { *x *= 1000; } }; println!("{:?}",v); }
Slices are references: all borrowing rules still apply!
- At most one mutable reference at a time
- No immutable references allowed with a mutable reference
- Many immutable references allowed simultaneously
#![allow(unused)] fn main() { // this won't work! let mut v = vec![1,2,3,4,5,6,7]; { let ref_1 = &mut v[2..5]; let ref_2 = &v[1..3]; ref_1[0] = 7; println!("{}",ref_2[1]); } }
#![allow(unused)] fn main() { // and this reordering will let mut v = vec![1,2,3,4,5,6,7]; { let ref_1 = &mut v[2..5]; ref_1[0] = 7; // ref_1 can be dropped let ref_2 = &v[1..3]; println!("{}",ref_2[1]); } }
Memory representation of slices
- Pointer
- Length

Let's return to &str?
&str is slice
-
&strcan be a slice of a string literal or a slice of aString -
&stritself (the reference) is stored on the stack, -
but the string data it points to can be in different locations depending on the context.
Let's break this down:
The &str Data (Various Locations)
The actual string data that &str points to can be in:
- Binary's read-only data segment (most common for string literals):
#![allow(unused)] fn main() { let s: &str = "hello"; // "hello" is in read-only memory println!("&s:{:p}", &s); println!("ptr: {:p}", s.as_ptr()); println!("len: {}", s.len()); // println!("capacity: {}\n", s.capacity()); // ERROR! Not applicable }
- Heap (when it's a slice of a
String):
#![allow(unused)] fn main() { let string = String::from("hello"); let s: &str = &string; // points to heap-allocated data println!("&s:{:p}", &s); println!("ptr: {:p}", s.as_ptr()); println!("len: {}", s.len()); }
True/False Statements on Rust Slices
A slice of type `&[i32]` is always immutable, even if it's created from a mutable array.
TRUE - "The slice is a reference to the array, which by default is immutable. Even if the source array is mutable, the slice is immutable." To get a mutable slice, you need to explicitly use `&mut [T]` syntax.
Slices in Rust consist of two components in memory: a pointer to the data and a length.
TRUE
You can have both an immutable slice and a mutable slice of the same vector active at the same time.
FALSE - Slices are references: all borrowing rules still apply!
The `&str` type is a slice, and the actual string data it points to is always stored in the binary's read-only data segment.
FALSE. While `&str` is indeed a slice, the string data it points to can be in different locations depending on the context, including the binary's read-only data segment (for string literals) or the heap (when it's a slice of a `String`).
Slices work with both arrays and vectors in Rust.
TRUE
Enter your answers into piazza poll.
Summary
- Slices are references to contiguous sub-sequences of elements in a collection
- Slices are immutable by default
- We can create mutable slices from mutable arrays
- Slices are references: all borrowing rules still apply!
&stris a slice of a string literal or a slice of aString&stritself (the reference) is stored on the stack, but the string data it points to can be in different locations depending on the context.