Introduction: Why Do You Need Regular Expressions in Rust?
Regular expressions (regex) are a powerful tool for searching, extracting, and replacing text based on patterns. In Rust, working with regular expressions is implemented through the regex crate, which is considered one of the fastest and safest in the ecosystem. Unlike many other languages, the Rust compiler checks the validity of a regular expression at compile time (if using the regex! macro), which helps avoid errors during execution.
In this article, we will explore practical examples of using regular expressions in Rust: from basic search to complex data extraction. You will learn how to set up a project, write patterns, and process results.
1. Project Setup and First Regex
Adding the Dependency
First, add the regex crate to your Cargo.toml file:
[dependencies]regex = "1"Then update the dependencies with the command cargo build.
First Example: Searching for a Word
Let's write a simple program that finds the word "Rust" in a string:
use regex::Regex;
fn main() { let re = Regex::new(r"Rust").unwrap(); let text = "I am learning Rust and Python."; if re.is_match(text) { println!("Word 'Rust' found!"); } else { println!("Word not found."); }}The Regex::new function compiles the pattern. If the pattern is invalid, the program will terminate with an error (use unwrap() only in prototypes).
2. Data Extraction Using Captures
Grouping and Named Groups
Regular expressions allow you to extract parts of text using parentheses. Consider an example with an email:
use regex::Regex;
fn main() { let re = Regex::new(r"(?P<name>[a-zA-Z0-9._%+-]+)@(?P<domain>[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})").unwrap(); let text = "My email: user@example.com, and also admin@test.org."; for caps in re.captures_iter(text) { println!("Name: {}", &caps["name"]); println!("Domain: {}", &caps["domain"]); }}In this example, we use named groups (?P<name>...). This makes the code more readable and convenient for further processing.
Extracting All Matches
If you need to get all matches without groups, use find_iter:
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "Prices: 100, 250, and 500 rubles."; for mat in re.find_iter(text) { println!("Found number: {}", mat.as_str()); }}The find_iter method returns an iterator over Match objects, which contain the start and end positions, as well as the matched text itself.
3. Text Replacement and Complex Patterns
Simple Replacement
Let's replace all digits with the word "NUMBER":
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "Code: 12345, status: 0."; let result = re.replace_all(text, "NUMBER"); println!("{}", result); // "Code: NUMBER, status: NUMBER."}Replacement Using Captures
You can reference groups in the replacement string using $1, $2, or named groups $name:
use regex::Regex;
fn main() { let re = Regex::new(r"(?P<year>\\d{4})-(?P<month>\\d{2})-(?P<day>\\d{2})").unwrap(); let text = "Date: 2024-03-15"; let result = re.replace_all(text, "$day.$month.$year"); println!("{}", result); // "Date: 15.03.2024"}Complex Pattern: Log Parsing
Let's look at a real-world example — parsing a web server log line:
use regex::Regex;
fn main() { let log_line =