Regex
The regex crate provides linear-time regex matching (no catastrophic backtracking like some engines).
Recipe
[dependencies]
regex = "1"use regex::Regex;
fn main() {
let re = Regex::new(r"^\d+$").unwrap();
println!("{}", re.is_match("8080"));
}When to reach for this: Log parsing, validation, extraction - prefer parse for simple numbers.
Working Example
use regex::Regex;
fn extract_ports(log: &str) -> Vec<u16> {
let re = Regex::new(r"port=(\d+)").unwrap();
re.captures_iter(log)
.filter_map(|c| c.get(1)?.as_str().parse().ok())
.collect()
}
fn main() {
let log = "port=8080 ok\nport=3000 ok";
println!("{:?}", extract_ports(log));
}What this demonstrates:
- Compile regex once, reuse
captures_iterfor groups- Combine with
parsefor typed output
Deep Dive
RegexBuilder for case insensitive, size limits. LazyLock static regex for global patterns.
Gotchas
- Compile in hot loop - Slow. Fix:
LazyLockoronce_cell. - unwrap Regex::new - Invalid pattern panics at runtime. Fix:
?in fn returning Result. - Regex for HTML/XML - Fragile. Fix: Proper parser.
- Overlapping matches -
find_iternon-overlapping only. - Unicode classes -
\wUnicode-aware - mind performance.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
split, parse | Simple patterns | Complex extraction |
nom / pest | Grammars | Quick regex ok |
fancy-regex | Lookaround needed | Linear time priority |
FAQs
compile cost?
Compile once reuse.case insensitive?
RegexBuilder::case_insensitive.replace?
replace_all with expansion syntax.split regex?
split method.bytes regex?
Regex::new on &[u8] patterns.unsafe?
Not needed - safe API.ReDoS?
regex crate designed safe.serde?
Store pattern str compile lazily.test?
assert captures.clippy?
trivial_regex warns simple patterns.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.