This post published on my blog before
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
Top comments (1)
what type of slice?
will be great to see example function signature with slice argument