Slices

Reviewed & published by Brayan K

Rust is a systems programming language focused on speed, memory safety, and fearless concurrency — and slices let you reference part of a collection without copying it.

Part of the free Rust course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll create string slices (&str) and array slices (&[T]), and write functions that return borrowed views into data.

What You'll Learn in This Lesson

1️⃣ String Slices

A string slice, written &str, references a portion of a String. You create one with a range: &s[0..5] borrows the first five bytes. The end index is exclusive, and you can drop the 0 or the end for handy shorthands.

fn main() {
    let s = String::from("hello world");

    // A string slice &str borrows part of a String. The range is [start..end],
    // where end is EXCLUSIVE. Indices are byte positions.
    let hello = &s[0..5];    // bytes 0,1,2,3,4 -> "hello"
    let world = &s[6..11];   // bytes 6..10      -> "world"
    println!("{} / {}", hello, world);   // -> hello / world

    // Shorthand: omit 0 at the start, or omit the end to go to the finish.
    let start = &s[..5];     // same as [0..5]
    let end = &s[6..];       // from 6 to the end
    let whole = &s[..];      // the entire string
    println!("{} | {} | {}", start, end, whole);
}

No data was copied here — each slice is a lightweight reference into the original s. That's why slices are so cheap to pass around.

2️⃣ Slices in Functions and on Arrays

Slices shine as function parameters and return types. A function returning the first word can hand back a &str that borrows the input — no allocation. The same idea applies to arrays: &[i32] is a slice of integers, letting one function work on a whole array or just part of it.

fn main() {
    let sentence = String::from("the quick brown fox");

    // first_word returns a slice that BORROWS from sentence — no copying.
    let word = first_word(&sentence);
    println!("First word: {}", word);   // -> First word: the

    // Slices also work on arrays. &[i32] is a slice of i32 values.
    let numbers = [10, 20, 30, 40, 50];
    let middle = &numbers[1..4];         // -> [20, 30, 40]
    println!("Middle: {:?}", middle);
    println!("Sum of middle: {}", sum(middle));   // -> Sum of middle: 90
}

// Taking &str (not &String) means this works for String AND literals.
fn first_word(text: &str) -> &str {
    let bytes = text.as_bytes();
    for (i, &byte) in bytes.iter().enumerate() {
        if byte == b' ' {            // b' ' is the byte value of a space
            return &text[..i];        // slice up to the first space
        }
    }
    &text[..]                         // no space: the whole string is one word
}

fn sum(slice: &[i32]) -> i32 {
    let mut total = 0;
    for n in slice {
        total += n;
    }
    total
}

Because first_word takes &str, it accepts a String, a literal, or another slice. And sum works on any slice of i32, whether from an array or a vector.

Your turn. Fill in the blanks marked ___, then run it.

// 🎯 YOUR TURN — finish the slices, then run it.
fn main() {
    let text = String::from("rust is fun");

    // 1) Slice out the first word "rust" (bytes 0 up to but not including 4).
    let first = &text[0..___];      // 👉 the exclusive end index for "rust"
    println!("first = {}", first);  // expected: first = rust

    // 2) Slice from byte 8 to the end to get "fun".
    let last = &text[___..];        // 👉 the start index for "fun"
    println!("last = {}", last);    // expected: last = fun

    // 3) Sum a slice of the middle three numbers.
    let nums = [1, 2, 3, 4, 5];
    let mid = &nums[1..4];          // [2, 3, 4]
    let total: i32 = mid.iter().sum();
    println!("total = {}", total);  // expected: total = 9
}

// ✅ Expected output:
//    first = rust
//    last = fun
//    total = 9

Mini-Challenge: A Text Inspector

Write a last_word function that returns a slice of the final word. Run it with cargo run.

// 🎯 MINI-CHALLENGE: a tiny text inspector.
//
// 1. Write fn last_word(text: &str) -> &str that returns the slice AFTER the
//    last space (the final word). Hint: scan for the last b' ' and slice from
//    just after it; if there is no space, return the whole text.
// 2. In main, call it on "hello there world" and print the result.
// 3. Also print the length of that last word with .len().
//
// ✅ Example output:
//    Last word: world
//    Length: 5

fn main() {
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Slices

GoalRust Syntax
String slice&s[0..5]
From start&s[..5]
To end&s[6..]
Whole thing&s[..]
Array slice&arr[1..4]
Slice param type&str / &[i32]

🎉 Lesson Complete!

Practice quiz

What is a slice in Rust?

  • A copy of a collection
  • A new heap allocation
  • A reference to a contiguous run of elements
  • A type of loop

Answer: A reference to a contiguous run of elements. A slice borrows a contiguous view into existing data without copying.

What type is a string slice written as?

  • &str
  • String
  • char
  • Vec<u8>

Answer: &str. A string slice has type &str.

Given s = "hello world", what is &s[0..5]?

  • world
  • hello world
  • ello
  • hello

Answer: hello. Bytes 0 through 4 form "hello"; the end index is exclusive.

Is the end index of a slice range inclusive or exclusive?

  • Inclusive
  • Exclusive
  • Depends on the type
  • It is the length

Answer: Exclusive. The end of a slice range is exclusive, so &s[0..5] excludes index 5.

What does the shorthand &s[..] produce?

  • The entire string
  • An empty slice
  • The first char
  • An error

Answer: The entire string. Omitting both ends slices the whole thing.

Why should functions take &str instead of &String?

  • It is faster to type
  • &String is invalid
  • &str accepts literals, slices, and Strings
  • It avoids the heap

Answer: &str accepts literals, slices, and Strings. &str is more general; a String coerces to &str automatically.

What is the type of a slice of i32 values?

  • [i32]
  • &[i32]
  • Vec<i32>
  • i32

Answer: &[i32]. An array slice of integers is &[i32].

Does creating a slice copy the underlying data?

  • Yes, always
  • Only for strings
  • Only for arrays
  • No, it just borrows a view

Answer: No, it just borrows a view. A slice is a pointer plus length; it borrows without copying.

What does &nums[1..4] give for nums = [10,20,30,40,50]?

  • [10, 20, 30]
  • [20, 30, 40]
  • [20, 30, 40, 50]
  • [30, 40]

Answer: [20, 30, 40]. Indices 1, 2, 3 (end exclusive) are 20, 30, 40.

What happens if you slice through the middle of a multi-byte UTF-8 character?

  • It rounds down
  • It returns None
  • The program panics
  • Nothing

Answer: The program panics. String slices must land on char boundaries or the program panics.

Continue this course

Frequently asked questions

What is a slice and why is it useful?

A slice is a reference to a contiguous run of elements inside a collection, rather than a copy of them. A string slice &str references part of a String's text; an array slice &[T] references part of an array or vector. Slices are useful because they let you pass around a window into data without copying it and without taking ownership — for example, returning the first word of a string as a slice that borrows from the original.

Why should functions take &str instead of &String?

A &str is more general than a &String. If a function accepts &str, you can pass it a string literal (which is already a &str), a slice of a String, or a whole String (which coerces to &str automatically). If it only accepted &String, you could not pass a literal. So taking &str makes your function usable in more situations with no downside, which is why it is the idiomatic choice for read-only string parameters.

Are slice ranges inclusive or exclusive at the end?

The end of a slice range is exclusive: &s[0..5] takes bytes at indices 0, 1, 2, 3, and 4 — five characters — but not index 5. You can omit the start (it defaults to 0) or the end (it defaults to the length), so &s[..5], &s[6..], and &s[..] are all valid shorthands. If you slice in the middle of a multi-byte UTF-8 character, the program will panic, because string slices must land on character boundaries.

Does slicing copy the data?

No. A slice is just a reference — internally a pointer to the starting element plus a length. Creating a slice does not allocate or copy the underlying data; it simply borrows a view of it. That makes slices very cheap and is why returning a slice from a function is a zero-cost way to hand back part of an existing value. Because it borrows, the original data must outlive the slice.