Start learning free

LESSON 61 OF 414 · STRINGS I

String Slicing in Rust: Range Slices with &s[a..b]

Lesson Read the full explanation

Not only we can select a single character from a string, but we can also select a sequence of consecutive characters (e.g. substring) from a string.

To achieve this, we use a range slice like this (the indexes are byte positions, which match characters for plain ASCII text):

&string[startIndex..endIndex];

This is called slicing a string. It returns the substring that starts at startIndex and ends just before endIndex.

Here is an example:

let language = "JavaScript";
let substring = &language[2..6];

println!("{}", substring); // Output: vaSc
println!("{}", language); // Output: JavaScript

Notice that language preserved its value. The slicing does not affect the original string. It just borrows a read-only view (a &str) of the part you asked for.


If you omit the end index, the slice extracts to the end of the string:

let language = "JavaScript";
let substring = &language[4..];

println!("{}", substring); // Output: Script

Assignment
Follow the Coding Tutorial and let's slice some strings!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 3
    Ctrl + Enter to check

    RUST · STRINGS I

    String Slicing in Rust, explained

    What slicing does

    A slice selects a run of consecutive characters from a string, the way you would cut a piece out of a ribbon. In Rust you write a range in square brackets and put a & in front:

    let language = "JavaScript";
    let substring = &language[2..6];
    println!("{}", substring);
    println!("{}", language);
    
    vaSc
    JavaScript
    

    The range 2..6 starts at position 2 and stops just before position 6, so it covers positions 2, 3, 4 and 5. Positions count from 0. The result is the four letters vaSc.

    A slice is a borrowed view

    The slice does not copy anything. The type of &language[2..6] is &str, a window onto the bytes of the original string. That is why the original is unchanged, and why the leading & is needed: without it, language[2..6] is a bare str of unknown size, and a variable can't hold that. If you want an independent string, copy the slice with .to_string(), which gives a String you can modify.

    Range forms

    You can leave out either end of the range:

    RangeMeaningOn "JavaScript"
    2..6from 2 up to, not including, 6vaSc
    4..from 4 to the endScript
    ..4from the start up to, not including, 4Java
    ..the whole stringJavaScript
    2..=5from 2 to 5, both includedvaSc

    The end of a normal range is excluded, so the length of a..b is b - a. An empty range such as 3..3 gives an empty string.

    Positions are bytes

    Rust measures a slice in bytes, not characters. For ASCII text such as English letters and digits, one character is one byte, so the numbers match what you count by eye. A character such as é takes two bytes. If a range starts or ends inside a character, the program panics at run time with a message saying the byte index is not a char boundary. If a position is beyond the end, or the start is greater than the end, it panics too:

    let f = "Avocado";
    let a = 4;
    println!("{}", &f[a..2]);
    
    begin <= end (4 <= 2) when slicing `Avocado`
    

    When the positions might be bad, use get, which returns an Option<&str>: message.get(5..40) is None for an 18-byte message instead of a panic.

    Computed positions

    The range ends can be any usize expression, not only literals. With let n = 12; the slice &message[..n] is the first 12 bytes, and &message[message.len() - 5..] is the last five. Since len() is the number of bytes, subtracting from it counts back from the end.

    The lesson's exercise

    The starter code gives message as "Coding feels great". The exercise cuts out three words.

    1. The first word. The first 6 characters sit at positions 0 to 5, so the range is 0..6.
    2. The middle word. The 8th through 12th characters are at positions 7 to 11, because positions start at 0. The range is 7..12, and it skips the space at position 6.
    3. The last word. The last 5 characters begin at position 13 and run to the end. Leaving the end off, 13.., means "to the end of the string".
    let message = "Coding feels great";
    let first_word = &message[0..6];
    let middle_word = &message[7..12];
    let last_word = &message[13..];
    

    These give Coding, feels and great. The whole message is 18 bytes long, and 13.. covers positions 13 to 17, which is five characters.

    Common mistakes

    • Including the end. Writing 0..5 for "the first six characters" gives only five. Take the last wanted position and add one.
    • Forgetting the &. let w = message[0..6]; is a compile error, because str has no size known at compile time.
    • Off-by-one on the space. The words in the exercise are separated by spaces, which have positions of their own. The space after Coding is at position 6, so the second word starts at 7.
    • Slicing text that is not ASCII with byte numbers computed by hand.

    Key takeaways

    • &s[a..b] borrows the bytes from a up to, but not including, b.
    • Either end may be left out; a.. runs to the end and ..b starts at the beginning.
    • The slice is a &str view and the original string does not change.
    • Bad ranges panic; get returns None instead.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free