Hi everyone. Before that, I wrote a post called Rust’s Borrowing and Reference Laws.
Today I’ll try to explain the slice type in Rust
Before starting, I’ll create a project with cargo;
cargo new slice_type
cd slice_type
Introduction
If you worked with languages like Python, you are familiar with slices. Slices don’t have ownership in Rust. When you work with slices, you think about a contiguous sequence of an element like a string.
For example, we’re using slices in Python like that;
py_string = 'Python'
# contains indices 0, 1 and 2
print(py_string[0:3]) # Pyt
We’re calling this as indexing syntax.
Let’s Back to the Rust Side
Let’s assume we have a function like below. In this section, we’ll use Rust’s example.
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (index, &item) in bytes.iter().enumerate() {
if item == b' ' {
return index;
}
}
s.len()
}
In the above example, we’ve created a function called first_word
. It doesn’t have ownership. So, that’s what we want. The Rust asking us what should we return? Is this a real question? I don’t think so. However, in this example, we’re trying to return an index or a length value for the whole string.
This function will return a byte index value according to example. We’re converting our string to an array of bytes using as_bytes()
method.
let bytes = s.as_bytes();
In the above line, we’ve created an array of bytes from a string. The next line is about iteration. We will not discuss the enumerate()
method in this post. For now, we should know that iter()
is a method that returns each element in a collection. So, the enumerate()
method wraps the result of iter and returns each element as part of a tuple instead.
for (i, &item) in
In the above section, we’re destructing a tuple. The first value of tuple is representing index
and the second is representing value
. Because we get a reference to the element from .iter().enumerate()
, we use &
in the pattern.
if item == b' ' {
return index;
}
In the above lines, we’re checking whether the item
equals to space or not. For example, if you passed Hello world!
, this block will run and return the last index. If you passed Helloworld!
, s.len()
will return. Let’s complete our example;
fn main() {
let s = String::from("Hello world");
let index = first_word(&s);
println!("Index {}", index);
}
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (index, &item) in bytes.iter().enumerate() {
if item == b' ' {
return index;
}
}
s.len()
}
Guess what will return 🙂 It will return 5. Because in this example, we’re looking for a space character. And that space is coming after o
.
String Slices
Each string consists of slices. I mean, each character is a slice. Let’s assume we have a string like that;
Hello world
. We also have slices by this example. These are;
INDEX | VALUE |
---|---|
0 | H |
1 | e |
2 | l |
3 | l |
4 | o |
5 | |
6 | w |
7 | o |
8 | r |
9 | l |
10 | d |
When you want to get a part of a string, you should use a range within brackets by specifying. For example;
let hello = String::from("Hello world");
let start_index = 0;
let last_index = 5;
let hello = &hello[start_index..last_index];
You can also use .get()
method. But &T
and .get()
are different. So, we know how we can get a range from a string. Let’s say we want to get a string after the specified letter. How can we do that?
fn main() {
let s = String::from("Hello world");
let letter = String::from(" ");
let index = first_word(&s, &letter);
println!("The string is {}", &s[index..]);
}
fn first_word(s: &String, l: &String) -> usize {
let bytes = s.as_bytes();
let letter = l.as_bytes();
for (index, &item) in bytes.iter().enumerate() {
if item == letter[0] {
return index;
}
}
s.len()
}
That’s what we want 🙂 You don’t have to specify ending or starting index always. You can use like that;
&your_var[start_index..];
&your_var[..end_index];
&your_var[..];
- The first example will get string starting the index you specified.
- The second example will get string until the index you specified.
- The last example will return the whole string.
Note: String slice range indices must occur at valid UTF-8 character boundaries. If you attempt to create a string slice in the middle of a multibyte character, your program will exit with an error.
Other Type Slices
String slices were strings. But slices can be used for other types. Let’s create a u8 array.
fn main() {
let number_array = [1, 5, 7, 9, 11, 13, 15, 17, 19, 21];
println!("Numbers: {:?}", &number_array[1..3]);
}
It will return Numbers: [5, 7]
.
So, according to this example, we can create other types of slices.
fn main() {
let an_array = [true, true, false, true, false, false, true, false];
println!("Array values: {:?}", &an_array[1..3]);
}
That’s all for now. If there is something wrong, let me know.
Thanks for reading