Start learning free

LESSON 66 OF 414 · STRINGS I

String Find in Rust: find(), rfind() and Option

Lesson Read the full explanation

In Rust, you can find the index of a substring using str::find():

s.find(sub)          // returns Some(index) of first occurrence, or None if not found
s.rfind(sub)         // returns Some(index) of last occurrence
s[start..].find(sub) // search starting from index `start` (the result counts from `start`)

Here are some examples:

let sentence = "Hello World";
let idx = sentence.find("World");
println!("{:?}", idx);  // Some(6)

if sentence.find("xyz").is_none() {
    println!("not found");
}

The index returned is the byte position of the first character of the found substring. Returns None if not found.


Assignment
Follow the Coding Tutorial and find the index of "World" in sentence!


Hint
Use sentence.find("World") and assign the result to idx.

STEP 1 OF 1
    Ctrl + Enter to check

    RUST · STRINGS I

    String Find in Rust, explained

    Searching for a substring

    To find where a piece of text occurs inside a string, use the method find. It looks for the first occurrence of the pattern you give it and reports the position where it starts:

    let sentence = "Hello World";
    let idx = sentence.find("World");
    println!("{:?}", idx);
    
    Some(6)
    

    The text World starts at position 6, counting from 0, so the result is Some(6). The {:?} in the format string prints a value in its debug form, which is how an Option is shown. A plain {} would not compile for it.

    The result is an Option

    The text might not be there at all, and Rust does not return a magic number such as -1 for that case. find returns an Option<usize>:

    • Some(i) when the pattern occurs; i is the byte position of its first character.
    • None when it doesn't.

    The compiler makes you deal with both cases before you can use the number, which prevents the classic bug of using -1 as a real position. Here are the usual ways to handle the result:

    let sentence = "Hello World";
    if sentence.find("xyz").is_none() {
        println!("not found");
    }
    match sentence.find("World") {
        Some(i) => println!("found at {}", i),
        None => println!("missing"),
    }
    if let Some(i) = sentence.find("llo") {
        println!("{}", &sentence[i..]);
    }
    
    not found
    found at 6
    llo World
    

    Use is_some() or is_none() for a yes-or-no test, match or if let to use the position, and unwrap_or(0) to supply a default. Calling unwrap() on a None panics, so reserve it for cases where the text is certain to be there. If you only need to know whether the text is present, sentence.contains("World") is simpler.

    What can be searched for

    The pattern can be a string slice, a single char, or a closure that tests one character. Searching is case-sensitive.

    • sentence.find("World") gives Some(6) and sentence.find("world") gives None.
    • sentence.find('o') gives Some(4), the first o.
    • sentence.find(|c: char| c.is_uppercase()) gives Some(0).
    • sentence.find("") gives Some(0), since an empty pattern matches at the start.

    Last occurrence and later occurrences

    rfind searches from the end, so it returns the position of the last occurrence: sentence.rfind('o') is Some(7), while find('o') is Some(4).

    To search from a given position, slice first: sentence[5..].find('o'). The result is relative to the slice, so it is Some(2), not the position in the full string. Add the start to get the real position:

    let start = 5;
    let abs = sentence[start..].find('o').map(|i| i + start);
    println!("{:?}", abs);
    
    Some(7)
    

    The map adds the start only when there is a Some, and leaves None as it is.

    Positions are byte offsets

    The number inside Some is a position in bytes, which is the kind of number slicing takes, so &s[i..] is always valid for a found i. For ASCII text it equals the character count. In "héllo wörld" the letter é takes two bytes, so find("w") gives Some(7), while chars().position(|c| c == 'w') gives Some(6), a character position. Use the form that matches what you do next.

    The lesson's exercise

    The starter code declares sentence as "Hello World". Below it, declare idx with let and assign it the result of find("World"):

            let sentence = "Hello World";
            let idx = sentence.find("World");
    

    Here idx is an Option<usize> holding Some(6), not the bare number 6. To get the number you would write idx.unwrap() or match on it.

    Key takeaways

    • s.find(pattern) returns Option<usize>: Some(byte position) of the first match, or None.
    • rfind gives the last match.
    • Searching a slice such as s[start..] returns a position relative to the slice.
    • Handle the Option with match, if let, is_some() or unwrap_or; unwrap() on None panics.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free