slice
As mentioned in the previous section, ownership can only be attributed to a variable, and using a reference makes it easy to get the value of a variable without obtaining or owning it. In addition to references, Rust has another data type that does not hold ownership: slices. Slicing allows us to refer to a contiguous sequence of elements in a collection rather than the entire collection.
String slice
A String slice is a reference to a contiguous part of a String, creating a String:
let s = String::from("hello world");
Copy the code
Create a string slice starting at index 0 and ending at index 5, but not including 5:
let s1 = &s[0.5]; // hello
// The simpler version is equivalent to the above
let s2 = &s[..5] // hello
Copy the code
Create a slice from index 6 to index 11:
let s3 = &s[6.11]; // world
// The simpler version is equivalent to the above
let s4 = &s[6..] // world
Copy the code
To create a complete slice:
let s5 = &str[...] ;// hello world
Copy the code
Try writing a function that uses slicing to get the first word of an English sentence:
fn first_word(s: &String) - > &str /* The slice type of the string */ {
// Convert a string to bytes, such as a single character a to 97
let bytes = s.as_bytes();
// We judge the first word by the first blank in a sentence
// Create a space literal byte, equivalent to 32
let blank = b' ';
// Create iterators with iter() and index the iterator members with enumerate()
// Each iteration yields a tuple containing the index and a reference to the corresponding member
for (i, &item) in bytes.iter().enumerate() {
// If the current byte is a space
// Then the letter before this space is the first word
if item == blank {
// Slice the first word and return it
return &s[0. i]; }}// No space is found, indicating that the passed argument is itself a word
// You can return the full slice directly
return&s[..] ; }Copy the code
Let’s use this function:
let s = String::from("hello world");
let word = first_word(&s);
println!("{}", word);
// hello
Copy the code
When a string is a complete word:
let s = String::from("hello");
let word = first_word(&s);
println!("{}", word);
// hello
Copy the code
String literals are slices
When we define a string literal, we create a string slice & STR. Since &str is an immutable reference, the string literal is naturally immutable:
let s = "hello world";
// Explicitly annotate the type
let s: &str = "hello world";
Copy the code
Take a slice of a string as an argument
Note that first_word requires &String, so we will get an error if we use slice & STR:
let s = "hello world";
first_word(&s) // error, expect &String, get & STR
Copy the code
For better function compatibility, we can use the slice type to accommodate both strings:
// Change the argument to slice type & STR
fn first_word(s: &str) - > &str {
/ * * / has been eliminated
}
first_word("hello world"); // hello
first_word(&String::from("hello world")); // hello
Copy the code
Other types of slices
In addition to strings, arrays also support array slicing, so create an array:
let a = [1.2.3.4.5];
Copy the code
Slice from start to index 2:
let a1 = &a[..2];
/ / [1, 2]
Copy the code
Slice from index 2 to end:
let a2 = &a[2. ] ;/ / [3, 4, 5]
Copy the code
Slice from index 1 to index 3:
let a3 = &a[1.3];
/ / [2, 3]
Copy the code
Storage of slices
The slice data structure internally stores a reference to the start position and a field describing the length of the slice. This field describing the length of the slice is equivalent to the end index minus the start index:
let s = String::from("hello world");
let world = &s[6..] // world
Copy the code
The following figure shows how the string S and sliced world are stored in memory. S refers to the character H of index 0, with length 11 and capacity 11. World refers to the character w of index 6, of length 5, and since world is only a slice reference to S, there is no capacity property: