Slice
- A slice is a reference to a contiguous (i.e. one after the other) sequence of elements in a collection or in a string.
- A slice is a kind of reference. Thus, it doesn't have ownership.
- Unlike the references used when we are borrowing, which is through an existing variable or a temp variable, the slice data structure internally stores the starting position and the length of the slice.
- You obtain a slice using
&[start_index..end_index]
where start index is the index of the first item in the slice end_index is one past the last element we want to include in the slice.
String slices
- A string slice is an immutable reference to a contiguous part of a
String
(i.e., theString
data type). - The data type for string slice is
&str
fn main() { let greeting = String::from("How are you?"); let part1: &str = &greeting[..3]; let part3 = &greeting[8..]; let part2 = &greeting[4..7]; let whole = &greeting[..]; println!("{part1}"); println!("{part3}"); println!("{part2}"); println!("{whole}"); }
The diagram below shows a representation of the slices in the snippet above.
- From the above snippet, each slice contains a pointer to the byte at the specified start position and the length of the slice.
- If you don't specify the first index but specify the end index, the slice begins from position 0 up to the end index minus 1.
- If you specify the start index but not the end index, the slice begins from the start index until the end of the string.
- If you specify the start and end index, the slice goes from the start index to the end index minus 1.
- If you don't specify the start and end indexes, then the slice gives you the entire string.
String literals are string slices
- In Rust, a string literal is also a string slice and the data type is also
&str
- just as it is for string slices created from theString
data type as shown above. - String literals are immutable
- String literals are hardcoded directly into the final executable/binary of your program. This is because a string literal is known at compile time. For more info, see memory allocation
Consider the below,
fn main() { let greeting = "hello world"; }
- The type of the variable
greeting
above is also&str
. That is, it is a slice pointing to specific point of the binary/executable. See string-literals for more info. This is why string literals are immutable.
String slices as a function parameter
- Given a function definition which has a string slice parameter i.e.,
&str
, you can pass it any of the following:- A string literal since this is of the same type as a string slice i.e.,
&str
- A slice of a string literal - this type will also be
&str
- A reference to a
String
- this makes sense since this is just like passing the full slice of thatString
- A slice of a
String
- this type will also be&str
- A string literal since this is of the same type as a string slice i.e.,
Example:
fn main() { let name = "Jack Jones"; print_name(name); // full string literal print_name(&name[0..4]); // slice of string literal let car = String::from("Mercedes Benz"); print_name(&car); //String reference which is equivalent to a slice of the whole Strong which is &car[..] print_name(&car[..]);// full slice of the String print_name(&car[9..]); // slice of the String } fn print_name(word: &str) { println!("{word}") }
Slices of collections
- You can also use slices with arrays and collections
fn main() { let fruits = ["apple", "pear", "orange", "mango", "pineapple"]; println!("{:?}", &fruits[1..3]); }
In the above, we specify &fruits[1..3]
which means we want elements from position 1 in the array up to position 2.
Thus, similar to string slices we specify a start index and end index and the result is elements from start index up to end index minus 1.
Slices of collections can be mutable
Unlike a String slice which is immutable, a slice of an array can be mutable if you wish.
This is because in case of an array you can index into it using the []
and mutate it as you wish.
Consider the snippet below:
fn main() { let mut games = ["basket", "ball"]; let games_slice1 = &games[..]; //immutable reference println!("{:?}", games_slice1); let games_slice2 = & mut games[0..1]; //mutable slice games_slice2[0] = "foot"; println!("{:?}", games); }