Rust Slices
Source: Dev.to

Most of my writings about Rust come from a JS/TS developer’s point of view. Intuitively, when you learn a new language through another, you tend to map concepts between the two. For example, a struct in Rust might map to an interface or type in TypeScript. Doing this makes it easier to consume knowledge in the new language because the concept suddenly feels familiar.
But this pattern of learning introduces a subtle problem: once you get used to constantly mapping ideas, it becomes harder to truly grasp concepts in the new language that don’t have a direct equivalent in the one you already know. This was the hurdle I ran into with slices in Rust.
This short article assumes a basic understanding of Rust concepts such as references, borrowing, and ownership.
A common Rust pattern
In Rust, it is generally best practice to use an immutable (read‑only) reference when writing a function that does not need to mutate its argument. To illustrate this, let’s write a function that accepts a full name and returns just the first name.
fn get_first_name(full_name: &str) -> &str {
full_name
.split_whitespace()
.next()
.unwrap_or("")
}
What’s going on here?
- The function borrows a string slice (
&str); it does not take ownership. - It splits the string by whitespace into words.
- It takes the first word (if any).
- If there are no words, it returns
"". - It returns a slice of the original string, not a new
String.
Let’s unwrap unpack 😌😉
What does “returning a slice” actually mean?
The function is not creating a new string. Instead, it is simply pointing to a portion of the existing one.
If our original string is "Lex Luthor", a slice could refer to:
"Lex""Luthor""thor""ex Lu"- or even
"Lex Luthor"(the entire string)
All of these are just views into the same underlying string data.
The JavaScript mental model (this helps)
If you’re coming from JavaScript, your brain probably jumps straight to substrings. A substring is a part of another string whose start and end indices fall within the bounds of the original string. If the start is 0 and the end is string.length, the substring represents the entire original string.
That’s conceptually how slices work in Rust too—but with one important difference:
Slices are references, not new values. This distinction is crucial.
Why slices exist at all
If you already have a reference to a string and you want to refer to part of that string, you could allocate a new string and copy the data into it. That would involve:
- allocating new memory
- copying bytes
- doing extra work you don’t actually need
Rust says: Why not just reference a portion of the original data instead? No copying, no new allocation, and everything stays fast and explicit. That is exactly why slices exist.
Slices aren’t just for strings
Slices work with arrays too, and the syntax looks very similar.
With strings
let original_value = String::from("Hello world");
let slice = &original_value[1..7];
slice is a &str pointing to "ello w" inside original_value.
With arrays
let ages = [13, 21, 37, 4, 55];
let slice = &ages[2..4];
slice is a reference to part of the array: [37, 4].
Slices use the form &original_value[start_index..end_index] where start_index is inclusive and end_index is exclusive. You can also omit one or both indices:
&value[..]→ the entire value&value[..3]→ from the start up to index 2&value[3..]→ from index 3 to the end
Final thoughts
Slices felt strange to me at first because I kept trying to force them into familiar JS concepts. Once I stopped thinking in terms of “new values” and started thinking in terms of references to existing data, everything clicked.
If you want to go deeper, the official Rust book explains slices beautifully and in more detail, including a powerful example of why you should use slices:
👉