Rust Regex Cheat Sheet
Quick-reference for the Rust regex crate organized by method. Includes compile-once patterns, capture groups, and practical extraction examples.
Adding the regex Crate
The regex crate is not in std. Add it to Cargo.toml before use.
[dependencies]
regex = "1"use regex::Regex;
fn main() {
let re = Regex::new(r"\d+").unwrap();
println!("{}", re.is_match("abc123")); // => true
}Regex::new() and Compile-Once Pattern
Regex::new() compiles a pattern and returns Result<Regex, Error>. Compile once and reuse to avoid repeated parsing.
use regex::Regex;
let re = Regex::new(r"^\d{3}-\d{4}$")?;
// Invalid patterns return Err:
let bad = Regex::new(r"[invalid");
assert!(bad.is_err());Use ? or unwrap() to handle the Result. Invalid patterns return a compile-time error description.
use std::sync::OnceLock;
use regex::Regex;
fn email_re() -> &'static Regex {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| Regex::new(r"[\w.+-]+@[\w-]+\.[\w.]+").unwrap())
}
// Call from anywhere — compiles once, reuses forever:
email_re().is_match("alice@example.com") // => trueOnceLock is the modern replacement for lazy_static. Thread-safe, zero-cost after first init, no macro needed.
// If targeting Rust < 1.70:
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref EMAIL_RE: Regex = Regex::new(r"[\w.+-]+@[\w-]+\.[\w.]+").unwrap();
}
EMAIL_RE.is_match("bob@test.org") // => trueis_match() — Boolean Check
Returns true if the pattern matches anywhere in the input string.
use regex::Regex;
let re = Regex::new(r"\d{3}-\d{4}").unwrap();
re.is_match("call 555-1234") // => true
re.is_match("no number here") // => falselet re = Regex::new(r"^\d+$").unwrap();
re.is_match("12345") // => true
re.is_match("123abc") // => false
re.is_match(" 123 ") // => falselet re = Regex::new(r"(?i)hello").unwrap();
re.is_match("Hello World") // => true
re.is_match("HELLO") // => truefind() — First Match
Returns Option<Match> with the matched text, start, and end byte offsets.
use regex::Regex;
let re = Regex::new(r"\d+").unwrap();
let m = re.find("abc 42 def").unwrap();
m.as_str() // => "42"
m.start() // => 4
m.end() // => 6let re = Regex::new(r"\d+").unwrap();
match re.find("no digits") {
Some(m) => println!("found: {}", m.as_str()),
None => println!("no match"),
}
// => "no match"find_iter() — All Matches
Returns an iterator over all non-overlapping matches.
use regex::Regex;
let re = Regex::new(r"\d+").unwrap();
let nums: Vec<&str> = re.find_iter("a1 b22 c333")
.map(|m| m.as_str())
.collect();
nums // => ["1", "22", "333"]let re = Regex::new(r"[A-Z][a-z]+").unwrap();
for m in re.find_iter("Alice met Bob and Carol") {
println!("{} at {}", m.as_str(), m.start());
}
// Alice at 0
// Bob at 10
// Carol at 18let re = Regex::new(r"\bthe\b").unwrap();
let count = re.find_iter("the cat and the dog").count();
count // => 2captures() — Capture Groups
Returns Option<Captures> with indexed access to each group.
use regex::Regex;
let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
let caps = re.captures("date: 2026-02-23").unwrap();
&caps[0] // => "2026-02-23" (full match)
&caps[1] // => "2026"
&caps[2] // => "02"
&caps[3] // => "23"caps[n] panics if the group did not participate. Use caps.get(n) for Option<Match> instead.
let re = Regex::new(r"(\w+)(?:\s+(\w+))?").unwrap();
let caps = re.captures("hello").unwrap();
caps.get(1).map(|m| m.as_str()) // => Some("hello")
caps.get(2).map(|m| m.as_str()) // => None (optional group)captures_iter() — All Capture Groups
Iterates over all non-overlapping capture groups in the input.
use regex::Regex;
let re = Regex::new(r"(\w+)=(\w+)").unwrap();
let input = "name=alice age=30 role=admin";
for caps in re.captures_iter(input) {
println!("{}: {}", &caps[1], &caps[2]);
}
// name: alice
// age: 30
// role: adminlet pairs: Vec<(&str, &str)> = re.captures_iter(input)
.map(|c| (c.get(1).unwrap().as_str(), c.get(2).unwrap().as_str()))
.collect();
pairs // => [("name", "alice"), ("age", "30"), ("role", "admin")]replace() and replace_all()
Substitutes matches with a replacement string. Returns Cow<str> — borrows when no match, allocates when changed.
use regex::Regex;
let re = Regex::new(r"\d+").unwrap();
let result = re.replace("item 42 costs 10", "##");
result // => "item ## costs 10"replace() returns Cow<str>. Call .to_string() or .into_owned() if you need a String.
let re = Regex::new(r"\d+").unwrap();
let result = re.replace_all("a1 b2 c3", "#");
result // => "a# b# c#"let re = Regex::new(r"(\w+)@(\w+)").unwrap();
let result = re.replace_all(
"alice@home bob@work",
"$1 at $2"
);
result // => "alice at home bob at work"let re = Regex::new(r"\d+").unwrap();
let result = re.replace_all("a1 b2 c3", |caps: ®ex::Captures| {
let n: i32 = caps[0].parse().unwrap();
(n * 10).to_string()
});
result // => "a10 b20 c30"Named Captures
Rust regex uses (?P<name>...) syntax for named groups. Access via .name() on the Captures struct.
use regex::Regex;
let re = Regex::new(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})").unwrap();
let caps = re.captures("2026-02-23").unwrap();
caps.name("year").unwrap().as_str() // => "2026"
caps.name("month").unwrap().as_str() // => "02"
caps.name("day").unwrap().as_str() // => "23"Rust regex uses (?P<name>...) (Python-style), not (?<name>...) (JavaScript-style).
let re = Regex::new(r"(?P<first>\w+) (?P<last>\w+)").unwrap();
let result = re.replace("John Smith", "$last, $first");
result // => "Smith, John"let re = Regex::new(r"(?P<key>\w+)=(?P<val>\w+)").unwrap();
for caps in re.captures_iter("host=localhost port=8080") {
let key = caps.name("key").unwrap().as_str();
let val = caps.name("val").unwrap().as_str();
println!("{key} -> {val}");
}
// host -> localhost
// port -> 8080Common Practical Patterns
Everyday extraction patterns using the regex crate.
use std::sync::OnceLock;
use regex::Regex;
fn email_re() -> &'static Regex {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| Regex::new(r"[\w.+-]+@[\w-]+\.[\w.]+").unwrap())
}
let text = "Contact alice@example.com or bob@test.org";
let emails: Vec<&str> = email_re().find_iter(text)
.map(|m| m.as_str())
.collect();
emails // => ["alice@example.com", "bob@test.org"]let re = Regex::new(r"https?://[\w.-]+(?:/[\w./?&=%#-]*)?").unwrap();
let text = "Visit https://example.com/docs?page=1 for info";
let urls: Vec<&str> = re.find_iter(text)
.map(|m| m.as_str())
.collect();
urls // => ["https://example.com/docs?page=1"]let digits_re = Regex::new(r"\D").unwrap();
let fmt_re = Regex::new(r"(\d{3})(\d{3})(\d{4})").unwrap();
let raw = "555.123.4567";
let digits = digits_re.replace_all(raw, "");
let formatted = fmt_re.replace(&digits, "($1) $2-$3");
formatted // => "(555) 123-4567"let re = Regex::new(
r"(?P<month>\d{2})/(?P<day>\d{2})/(?P<year>\d{4})"
).unwrap();
if let Some(caps) = re.captures("Event on 02/23/2026") {
let y = caps.name("year").unwrap().as_str();
let m = caps.name("month").unwrap().as_str();
let d = caps.name("day").unwrap().as_str();
println!("{y}-{m}-{d}"); // => 2026-02-23
}