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: