Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Cheat Sheets
  3. Rust
  4. Rust Regex Cheat Sheet
RustCheat Sheet

Rust Regex Cheat Sheet

Quick-reference for the Rust regex crate organized by method. Includes compile-once patterns, capture groups, and practical extraction examples.

On this page
  1. 1Adding the regex Crate
  2. 2Regex::new() and Compile-Once Pattern
  3. 3is_match() — Boolean Check
  4. 4find() — First Match
  5. 5find_iter() — All Matches
  6. 6captures() — Capture Groups
  7. 7captures_iter() — All Capture Groups
  8. 8replace() and replace_all()
  9. 9Named Captures
  10. 10Common Practical Patterns
Adding the regex CrateRegex::new() and Compile-Once Patternis_match() — Boolean Checkfind() — First Matchfind_iter() — All Matchescaptures() — Capture Groupscaptures_iter() — All Capture Groupsreplace() and replace_all()Named CapturesCommon Practical Patterns

Adding the regex Crate

The regex crate is not in std. Add it to Cargo.toml before use.

Cargo.toml dependency
[dependencies]
regex = "1"
Basic import
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.

Regex::new() returns Result
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.

OnceLock compile-once pattern (Rust 1.70+)
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")  // => true

OnceLock is the modern replacement for lazy_static. Thread-safe, zero-cost after first init, no macro needed.

Legacy: lazy_static alternative
// 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")  // => true

is_match() — Boolean Check

Returns true if the pattern matches anywhere in the input string.

Basic is_match()
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") // => false
Anchor for full-string match
let re = Regex::new(r"^\d+$").unwrap();
re.is_match("12345")    // => true
re.is_match("123abc")   // => false
re.is_match("  123  ")  // => false
Case-insensitive with inline flag
let re = Regex::new(r"(?i)hello").unwrap();
re.is_match("Hello World")  // => true
re.is_match("HELLO")        // => true

find() — First Match

Returns Option<Match> with the matched text, start, and end byte offsets.

find() returns Option<Match>
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()     // => 6
Handle no match
let 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.

Collect all 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"]
Loop over matches
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 18
Count matches
let re = Regex::new(r"\bthe\b").unwrap();
let count = re.find_iter("the cat and the dog").count();
count  // => 2

captures() — Capture Groups

Returns Option<Captures> with indexed access to each group.

Indexed access with caps[n]
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.

Safe access with .get()
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.

Extract structured data
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: admin
Collect into a Vec of tuples
let 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.

replace() — first match only
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.

replace_all() — every match
let re = Regex::new(r"\d+").unwrap();
let result = re.replace_all("a1 b2 c3", "#");
result  // => "a# b# c#"
Capture group references in replacements
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"
Closure-based replacement
let re = Regex::new(r"\d+").unwrap();
let result = re.replace_all("a1 b2 c3", |caps: &regex::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.

Define and access named groups
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).

Named groups in replacements
let re = Regex::new(r"(?P<first>\w+) (?P<last>\w+)").unwrap();
let result = re.replace("John Smith", "$last, $first");
result  // => "Smith, John"
Iterate with named captures
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 -> 8080

Common Practical Patterns

Everyday extraction patterns using the regex crate.

Email extraction with compile-once
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"]
URL extraction
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"]
Phone number normalization
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"
Date parsing with named captures
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
}
Learn Rust in Depth
Rust Strings Practice →
See Also
String Types →Error Handling →

Start Practicing Rust

Free daily exercises with spaced repetition. No credit card required.

← Back to Rust Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.