Slices in Rust
Tags: rust, beginners, programming, tutorial
Published: 2024-03-05 14:09:58

## The Slice type in Rust

Rust has a built-in type called `Slice` that is used to reference a contiguous sequence of elements in a collection. Slices are a very important concept in Rust, and they are used extensively in the standard library. In this lesson, we will explore the `Slice` type and how it is used in Rust.

If you prefer a video version
{% youtube dKymZbFp0ZQ %}

<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/dKymZbFp0ZQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->

All the code is available on [GitHub](https://youtu.be/dKymZbFp0ZQ) (link available in the video description)

## What is a Slice?

A slice is a reference to a contiguous sequence of elements in a collection. 

Slices are used to reference a portion of a collection, and they are used extensively in the standard library. 

Slices are a very important concept in Rust, and they are used extensively in the standard library. In this lesson, we will explore the `Slice` type and how it is used in Rust.

## A first example

Let's see a simple example of a slice. We will use an array:

```rust
fn main() {
    let a = ["a", "b", "c", "d", "e"];
    let slice = &a[1..3];
    println!("{:?}", slice);
}
```

In this example, we have an array `a` with five elements. We then create a slice `slice` that references the second and third elements of the array.

When we run the program, we get the following output:

```rust
["b", "c"]
```

The Range `1..3` is used to create the slice. The first number is the starting index, and the second number is the ending index. 

The ending index is exclusive, which means that the element at the ending index is not included in the slice.

## A second example

Let's try a slice of a vector of integers:

```rust
fn main() {
    let v = vec![10, 20, 30, 40, 50];
    let slice = &v[3..4];
    println!("{:?}", slice);
}
```

In this example, we have a vector `v` with five elements. We then create a slice `slice` that references the fourth element of the vector.

When we run the program, we get the following output:

```rust
[40]
```

## Third Example

Let's try a slice of a string:

```rust
fn main() {
    let s = String::from("hello world");
    let hello = &s[0..5];
    let world = &s[6..11];
    println!("{:?}", hello);
    println!("{:?}", world);
}
```

In this example, we have a string `s` with the value "hello world". We then create two slices, `hello` and `world`, that reference the first five and last five characters of the string, respectively.

When we run the program, we get the following output:

```rust
"hello"
"world"
```

[![Slices in Rust - Rust programming tutorial](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/895gl86olb3thkleo78o.png)](https://youtu.be/dKymZbFp0ZQ)

## Range shortcuts for slices

There are some shortcuts for creating slices. For example, if you want to start at index 0, you can omit the first number:

```rust
let s = String::from("hello");
let slice = &s[0..2];
let slice = &s[..2];
```

Will have as an output:

```rust
"he"
```
Also, if you want to go to the end of the string, you can omit the second number:

```rust
let s = String::from("hello");
let len = s.len();
let slice = &s[3..len];
let slice = &s[3..];
```

Will have as an output:

```rust
"lo"
```

You can also get the entire string by using the `..` syntax:

```rust
let s = String::from("hello");
let len = s.len();
let slice = &s[0..len];
let slice = &s[..];
```

Will have as an output:

```rust
"hello"
```

## Exercise (without Slices)

Let's write a program that takes a string and returns the first word in the string. 

First, let'simplement a solution without using slices.

Here is the code:

```rust
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    println!("the first word is: {}", word);
}


fn first_word(s: &String) -> usize {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }

    s.len()
}
```

Explanation:

- The `as_bytes` method returns a slice of the string's bytes.
- The `iter` method returns an iterator over the slice.
- The `enumerate` method returns an iterator that yields the index and the value of each element in the slice.
- The `b' '` syntax is used to create a byte literal.
- The `s.len()` method returns the length of the string.

We get this output:

```rust
the first word is: 5
```

In  this case, `5` is the index of the first space in the string, which is the end of the first word.

But there is a problem with this code. If we try to clear the string `s` after calling the `first_word` function, we will NOT get a compile error. The variable `word` will still have the value 5, even though the string `s` has been cleared.


```rust
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s); // word will get the value 5
    println!("the s is: {}", s);
    println!("the first word is: {}", word);

    s.clear(); // this empties the String, making it equal to ""
    println!("the s is: {}", s);
    println!("the first word is: {}", word);    
}
```

The output will be:

```rust
the s is: hello world
the first word is: 5
the s is:
the first word is: 5
```

The `word` variable still has the value 5, but the string `s` has been cleared. 

Let's see how we can fix this using slices.


## Exercise (using slices)

Let's go back to our programming example.

Using slices, we can rewrite the `first_word` function like this:

```rust
fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}
```

Now, the `first_word` function returns a string slice, which is a reference to part of the original string. 

If now we have somerthing like this:

```rust
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s); // word will get the value 5
    println!("the s is: {}", s);
    println!("the first word is: {}", word);

    s.clear(); // this empties the String, making it equal to ""
    println!("the s is: {}", s);
    println!("the first word is: {}", word);    
}
```

We will get a compile error, because we are trying to use `word` after `s` has been cleared.

## String literals as Slices

Recall that we talked about string literals being stored inside the binary.

Now that we know about slices, we can understand string literals.

Update the code in the `main` function to use a string literal below the `s` variable (you can keep the lines that use `s` as well). :

```rust
fn main() {
    let s = String::from("hello world");
    let word = first_word(&s); // word will get the value 5
    println!("the s is: {}", s);
    println!("the first word is: {}", word);

    let s2 = "hello world";
    let word2 = first_word(&s2); // word will get the value 5
    println!("the s is: {}", s2);
    println!("the first word is: {}", word2);
}

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}
```

The type of `s` is `&str`, which is a slice of a string. This means that `s` is a reference to a contiguous sequence of characters in memory.

As a final note, the type of `s2` is `&str`, which is a slice of a string. This means that `s2` is a reference to a contiguous sequence of characters in memory.

So we can remove the `&s2` from the `first_word` function and the code will still work.

## Conclusion

In this lesson, we explored the `Slice` type in Rust. We learned that a slice is a reference to a contiguous sequence of elements in a collection. 

We also learned how to create slices using the `Range` type and how to use slices to reference parts of a string.

In the next lesson, we will see the `Struct` type in Rust.

If you prefer a video version
{% youtube dKymZbFp0ZQ %}

<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/dKymZbFp0ZQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->

All the code is available on [GitHub](https://youtu.be/dKymZbFp0ZQ) (link available in the video description)