Strings and Text Processing

find() and matches()

str provides pattern-searching methods that locate the byte offset of a match (.find(), .rfind()) or iterate over all non-overlapping matches (.matches(), .match_indices()).

Reference of Search and Match Methods on str

Method Search Direction Return Type What It Yields
s.find(pat) Left to right Option<usize> Starting UTF-8 byte index of the first match
s.rfind(pat) Right to left Option<usize> Starting UTF-8 byte index of the last match
s.matches(pat) Left to right Matches<'_, P> Iterator of matched &str substrings
s.rmatches(pat) Right to left RMatches<'_, P> Iterator of matched &str substrings from the right
s.match_indices(pat) Left to right MatchIndices<'_, P> Iterator of (usize, &str) ((byte_offset, matched_slice))
s.rmatch_indices(pat) Right to left RMatchIndices<'_, P> Iterator of (usize, &str) from the right

Locating Byte Offsets with .find() and .rfind()

Every index returned by .find() or .rfind() is guaranteed to be a valid UTF-8 character boundary suitable for string slicing:

fn main() {
    let path = "/home/prime/projects/rust/Cargo.toml";

    if let Some(first_slash) = path.find('/') {
        println!("First '/' at byte {first_slash}");
    }

    if let Some(last_slash) = path.rfind('/') {
        let dir = &path[..last_slash];
        let file = &path[last_slash + 1..];
        println!("Dir:  '{dir}'");
        println!("File: '{file}'");
    }
}
First '/' at byte 0
Dir:  '/home/prime/projects/rust'
File: 'Cargo.toml'

Extracting All Matches and Their Offsets with .matches() and .match_indices()

fn main() {
    let log = "err[E0308] at line 4, err[E0502] at line 19";

    // 1. Collect all substrings matching the digit predicate:
    let digits: Vec<&str> = log.matches(|c: char| c.is_ascii_digit()).collect();
    println!("Individual digits: {digits:?}");

    // 2. Find every occurrence of "err" along with its starting byte offset:
    for (byte_pos, matched) in log.match_indices("err") {
        let code = &log[byte_pos..byte_pos + 10];
        println!("Matched '{matched}' at byte {byte_pos:>2} -> {code}");
    }
}
Individual digits: ["0", "3", "0", "8", "4", "0", "5", "0", "2", "1", "9"]
Matched 'err' at byte  0 -> err[E0308]
Matched 'err' at byte 22 -> err[E0502]