Rust Text Patterns
A pattern is the thing a search method looks for in text. A pattern can be quoted text, one character, a set of characters, or a test that a character passes.
Three kinds of pattern, one method
contains accepts text, a set of chars, or a test — the call shape never changes:
println!("{}", "rust is fun".contains("is"));
println!("{}", "hello".contains(&['x', 'l'][..]));
println!("{}", "abc1".contains(char::is_numeric));find gives a byte position
The é in "héllo" takes two bytes, so the first l sits at byte 3, not letter 2:
println!("{:?}", "héllo".find('l'));
println!("{:?}", "héllo".find('z'));A test can be the pattern
Hand a char test where a pattern is asked for, and the first character that passes it is the match:
println!("{:?}", "error 42".find(char::is_numeric));
println!("{}", "9lives".starts_with(char::is_numeric));matches finds them all
matches collects every match. match_indices adds the byte position of each:
println!("an count = {}", "banana".matches("an").count());
let pairs: Vec<(usize, &str)> = "héllo".match_indices('l').collect();
println!("{:?}", pairs);starts_with and ends_with take patterns too
The same idea works at the edges, with any pattern kind:
println!("{}", "photo.png".ends_with(".png"));
println!("{}", "file.rs".starts_with(&['f', 'x'][..]));Example
fn main() {
let line = "error at line 42: disk full";
println!("has error = {}", line.contains("error"));
println!("first digit at byte = {:?}", line.find(char::is_numeric));
println!("colon at byte = {:?}", line.find(':'));
let vowels = "rust is fun".matches(&['a', 'e', 'i', 'o', 'u'][..]).count();
println!("vowels = {}", vowels);
let pairs: Vec<(usize, &str)> = "héllo".match_indices('l').collect();
println!("l pairs = {:?}", pairs);
}Key points
- Whatever its kind, a pattern answers one question: does this piece of text match?
- A pattern can be a &str: the whole text must appear in a row.
- A pattern can be a char, matching that one character.
- A pattern can be a set of chars like &['a', 'e']: any one of them.
- A pattern can be a test like char::is_numeric: any character the test accepts.
- Methods that take a pattern: contains, starts_with, ends_with, find, rfind, matches, match_indices, split and its family, replace, and trim_matches
- find hands back the BYTE position of the first match as an Option — count it in bytes, not letters
- rfind searches from the end and finds the last match
- matches walks every match.
- match_indices walks each match with its byte position.
- A plain pattern matches fixed text or one character at a time.
- A plain pattern cannot describe a shape, like "four digits, a dash, two digits".
In this example, one log line is searched four ways. contains uses a text pattern and answers true. find with the test char::is_numeric locates the first digit: byte 14, where the 42 begins. find with a char pattern locates the colon at byte 16. Then matches with a set-of-chars pattern counts the vowels in "rust is fun": u, i, u — three. Last, match_indices lists both l letters of "héllo" with their byte positions, 3 and 4. The first sits at byte 3 because é takes bytes 1 and 2. One idea, four methods.
Note
The pattern type is never written by hand.
The Pattern trait behind it cannot even be named on stable Rust yet.
A char, a set, a text or a test can be passed wherever a method asks for a pattern.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What does "hello".find(&['x', 'l'][..]) return?