Learning Rust 🦀: 09 - Ownership: The Slice type
Tags: learning, rust, programming, development
Published: 2023-08-12 13:43:24

This is the last article for Ownership in this 4-article mini-series. We will close our **references** discussion with a special kind of reference, the **Slice Type**.

> Please check the previous 3 articles in this series to recap what we have discussed about Ownership. 

>#### ⚠️ Remember!
You can find all the code snippets for this series in its [accompanying repo](https://github.com/FadyGrAb/rust-learning-series)  
If you don't want to install Rust locally, you can play with all the code of this series in the official [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021) that can be found on its official page.

> ⚠️⚠️ The articles in this series are loosely following the contents of "The Rust Programming Language, 2nd Edition" by Steve Klabnik and Carol Nichols in a way that reflects my understanding from a Python developer's perspective.

⭐ I try to publish a new article every week (maybe more if the Rust gods 🙌 are generous 😁) so stay tuned 😉. I'll be posting "new articles updates" on my [LinkedIn](https://www.linkedin.com/in/fady-abdelmassih) and [Twitter](https://twitter.com/fadyGrAb).

## Table of Contents:
- [The Slice type](#the-slice-type)
- [A programming problem](#a-programming-problem)
- [Solving the problem without the Slice](#solving-the-problem-without-the-slice)
- [String Slices](#string-slices)
- [Solving the problem with the Slice](#solving-the-problem-with-the-slice)
- [Other Slices](#other-slices)

### The Slice type:
As I've mentioned in the article's intro, **The Slice Type** is a kind of reference. What it does is that it lets us reference a sequence of elements in a collection rather than the whole thing. It is similar to Python's ":" operator like in `slice = collection[3:5]` where the `3` is the start index (inclusive) and the `5` is the end index (exclusive, 4 is the actual index of the last return item from the collect)

### A programming problem:
If you are coming from Python like me, you would think that there isn't anything special about *Slices* but there is more than meets the eye about them in Rust! 😉  
To better demonstrate that, let's try to solve the following problem:
> Write a function that returns the first word in a sentence. And if the sentence is comprised of only one word, return the whole sentence.

### Solving the problem without the Slice:
First, we will solve the problem without the use of Slices. As of this point, we don't know how to use them anyway 😁.
Here is the code:
```rust
fn main() {
    let mut s = String::from("Hello this is a test sentence");
    let index = find_word(&s);
    println!("Sentence is '{s}' before clear");
    s.clear();
    println!("Sentence is '{s}' after clear.");
    println!("The first word ends at index: {}", index - 1);
}

fn find_word(sentence: &String) -> usize {
    let bytes = sentence.as_bytes();
    for (idx, &byte) in bytes.iter().enumerate() {
        if byte == b' ' {
            return idx;
        }
    }
    sentence.len()
}
```
> If some of the code looks unfamiliar now, don't worry as we will cover everything later. But you should get a general sense of what the code is doing.

As we don't know how to return part of the *String* just yet, we will return the end index of the first word instead. So, basically the `find_word` function search every character in the passed string for a space (`b' '`) then returns its index if found or returns the string length if not.  
This code will compile just fine but will introduce a critical bug into your program 😯!  
If you haven't figured out it yet, have a look at the code output and have another go:
```sh
fady@vm:/home/code/find_word_without_slice$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/find_word_without_slice`
Sentence is 'Hello this is a test sentence' before clear
Sentence is '' after clear.
The first word ends at index: 4
```
The `s.clear()` clears out `s` (similar to `s = ""`). But the `index` is still `4` (the space index after "Hello") even after `s` was cleared. You see where I'm going, right? If you use the `index` later in your program to do anything with the first word of the sentence, it will panic (runtime error)!

### String Slices:
Now we will know how to return - or rather reference - part of a *String*.
```rust
fn main() {
    let s = String::from("Hello this is a test sentence");
    let word1 = &s[0..5];
    let word2 = &s[..5];
    println!(
        "The first word is word1: '{}' and is also word2: '{}'",
        word1, word2
    );
    let ln = s.len();
    let word3 = &s[21..ln];
    let word4 = &s[21..];
    println!(
        "The last word is word3: '{}' and is also word4: '{}'",
        word3, word4
    );
    let word5 = &s[..];
    println!(
        "The whole sentence is s: '{}' and is also word5: '{}'",
        s, word5
    )
}
```
If you are familiar with Python slicing, you will find Rust String Slices familiar too. The main difference is that Rust Slices return reference to the characters sequence.
> If you try to slice a string in the middle of a multi-byte character like some UTF-8 characters, your code will error out. We will see how to handle UTF-8 encoded characters later.

### Solving the problem with the Slice:
As we saw in our solution, decoupling `index` from `s` may result in hidden bugs further down in our code. We will refactor our previous solution using *String Slices* as follows:
```rust
fn main() {
    let mut s = String::from("Hello this is a test sentence");
    let word = find_word(&s);
    println!("Sentence is '{s}' before clear");
    s.clear();
    println!("First word is {word}");
    println!("Sentence is '{s}' after clear.");
}
fn find_word(sentence: &str) -> &str {
    let bytes = sentence.as_bytes();

    for (idx, &byte) in bytes.iter().enumerate() {
        if byte == b' ' {
            return &sentence[..idx];
        }
    }
    &sentence[..]
}
```
We now instead of returning the end index of the first word, we will return the actual first word using **String Slices**.  
This seems ok, right? Wrong!  
When you run this code, it will produce the following compile error:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hssb8f6ye2qw80w6t7ag.png)

let's break down this error. First the error message is *"cannot borrow `s` as mutable because it is also borrowed as immutable"*. If you remember from the last article, you can have either only one **mutable** reference or multiple **immutable** references at a time. What happens here is that we passed `s` as an immutable reference to `find_word` to get the first word and returned also an immutable slice which is a reference. Then we called `s.clear()` which passes `s` as a mutable reference with the intention to clear `s`. And as Rust always ensures that the reference will be valid, when we try to use `word` after `s.clear()`, the compiler complains as this violates one of the references rules we have mentioned earlier.  
The bottom line here is that the *String Slice* (`word`) is tightly coupled with the *String* (`s`) which means that we can safely assume that `word` will always reference the first word in `s`. And if we tried to change `s` before `word` goes out of scope, the compiler will complain. We still have the same bug but **Slices** made its discovery happens sooner. And this is again how Rust enforces memory safety using Ownership!  

### Other Slices:
We can slice Rust arrays similar to String Slices:
```rust
fn main() {
    let col = [10, 20, 30, 40, 50];
    let slice = &col[2..4];
    assert_eq!(slice, &[30, 40]);
}
```
Note that the program will panic if the assertion fails. Also note that this array is of type [i32] which can be sliced like a String.  

Phewww 😮‍💨, Rust's Ownership is ... an unconventional topic to understand, especially if you are coming from Python World! After this 4-article mini-series, we now have a basic understanding about:
    - What is Rust's Ownership.
    - What is memory safety and how Rust uses the Ownership principle to enforce it.
    - What is borrowing and what are the reference rules.
    - How to use the Slice Type.

In the next article, we will start discussing another interesting Rust feature, **Structs**! See you then 👋.