Expressões regulares (regex) são poderosas para buscar, validar e transformar texto. A crate regex do Rust é conhecida por sua performance excepcional e segurança — ela garante tempo de execução linear, sem risco de backtracking catastrófico. Nesta receita, você vai aprender a usar regex de forma eficiente em Rust.
Dependências
[package]
name = "receita-regex"
version = "0.1.0"
edition = "2021"
[dependencies]
regex = "1"
Código Completo
use regex::Regex;
use std::sync::LazyLock;
// =============================================
// Regex compilada uma vez com LazyLock (recomendado)
// =============================================
static RE_EMAIL: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap()
});
static RE_TELEFONE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"\(?\d{2}\)?\s*\d{4,5}-?\d{4}").unwrap()
});
fn main() {
// =============================================
// 1. is_match() — verificar se há correspondência
// =============================================
let re = Regex::new(r"\d{3}\.\d{3}\.\d{3}-\d{2}").unwrap();
println!("=== is_match ===");
let cpfs = vec!["123.456.789-00", "12345678900", "abc.def.ghi-jk"];
for cpf in &cpfs {
println!(" '{}' é CPF formatado? {}", cpf, re.is_match(cpf));
}
// =============================================
// 2. find() — encontrar a primeira correspondência
// =============================================
println!("\n=== find ===");
let texto = "Meu telefone é (11) 98765-4321 e o fixo é (11) 3456-7890";
if let Some(m) = RE_TELEFONE.find(texto) {
println!("Primeiro telefone: '{}' (posição {}-{})", m.as_str(), m.start(), m.end());
}
// find_iter() — encontrar todas as correspondências
println!("\n=== find_iter ===");
for m in RE_TELEFONE.find_iter(texto) {
println!(" Encontrado: '{}'", m.as_str());
}
// =============================================
// 3. captures() — extrair grupos
// =============================================
println!("\n=== captures ===");
let re_data = Regex::new(r"(\d{2})/(\d{2})/(\d{4})").unwrap();
let texto = "Nascimento: 15/06/1990, Cadastro: 23/02/2026";
for cap in re_data.captures_iter(texto) {
println!(
" Data: {} | Dia: {} | Mês: {} | Ano: {}",
&cap[0], &cap[1], &cap[2], &cap[3]
);
}
// Grupos nomeados
let re_nome = Regex::new(
r"(?P<nome>[A-Z][a-záéíóúãõç]+)\s+(?P<sobrenome>[A-Z][a-záéíóúãõç]+)"
).unwrap();
let texto = "Contato: Ana Silva e Carlos Santos";
for cap in re_nome.captures_iter(texto) {
println!(
" Nome: {} | Sobrenome: {}",
&cap["nome"], &cap["sobrenome"]
);
}
// =============================================
// 4. replace() — substituir texto
// =============================================
println!("\n=== replace ===");
// Substituir primeira ocorrência
let re = Regex::new(r"\bRust\b").unwrap();
let resultado = re.replace("Eu amo Rust e uso Rust todo dia", "Ferrugem");
println!(" replace: {}", resultado);
// Substituir todas as ocorrências
let resultado = re.replace_all("Eu amo Rust e uso Rust todo dia", "Ferrugem");
println!(" replace_all: {}", resultado);
// Substituição com referência a grupos capturados
let re = Regex::new(r"(\d{2})/(\d{2})/(\d{4})").unwrap();
let resultado = re.replace_all(
"Data: 23/02/2026",
"$3-$2-$1" // Referenciar grupos por número
);
println!(" BR->ISO: {}", resultado);
// Substituição com closure
let re = Regex::new(r"\b[a-z]+\b").unwrap();
let resultado = re.replace_all("olá mundo rust", |caps: ®ex::Captures| {
caps[0].to_uppercase()
});
println!(" Maiúsculas: {}", resultado);
// =============================================
// 5. split() — dividir texto por padrão
// =============================================
println!("\n=== split ===");
let re = Regex::new(r"[,;\s]+").unwrap();
let texto = "maçã, banana; cereja damasco, uva";
let partes: Vec<&str> = re.split(texto).collect();
println!(" Partes: {:?}", partes);
// =============================================
// 6. Validação com regex LazyLock
// =============================================
println!("\n=== Validação com LazyLock ===");
let emails = vec![
"usuario@exemplo.com",
"invalido@",
"nome.sobrenome@empresa.com.br",
"@sem-nome.com",
];
for email in &emails {
println!(" '{}' válido? {}", email, RE_EMAIL.is_match(email));
}
// =============================================
// 7. Extrair dados estruturados de texto
// =============================================
println!("\n=== Extrair dados ===");
let log = r#"
[2026-02-23 14:30:15] INFO: Servidor iniciado na porta 8080
[2026-02-23 14:30:16] WARN: Cache expirado, recarregando
[2026-02-23 14:30:17] ERROR: Conexão recusada pelo banco
"#;
let re_log = Regex::new(
r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+)"
).unwrap();
for cap in re_log.captures_iter(log) {
println!(
" Hora: {} | Nível: {} | Msg: {}",
&cap[1], &cap[2], cap[3].trim()
);
}
// =============================================
// 8. Case-insensitive e flags
// =============================================
println!("\n=== Flags ===");
// (?i) — case insensitive
let re = Regex::new(r"(?i)rust").unwrap();
let texto = "Rust, RUST, rust, RuSt";
let matches: Vec<&str> = re.find_iter(texto).map(|m| m.as_str()).collect();
println!(" Case insensitive: {:?}", matches);
// (?m) — multiline (^ e $ por linha)
let re = Regex::new(r"(?m)^\d+").unwrap();
let texto = "123 abc\n456 def\n789 ghi";
let matches: Vec<&str> = re.find_iter(texto).map(|m| m.as_str()).collect();
println!(" Multiline: {:?}", matches);
}
Saída do Programa
=== is_match ===
'123.456.789-00' é CPF formatado? true
'12345678900' é CPF formatado? false
'abc.def.ghi-jk' é CPF formatado? false
=== find ===
Primeiro telefone: '(11) 98765-4321' (posição 15-30)
=== find_iter ===
Encontrado: '(11) 98765-4321'
Encontrado: '(11) 3456-7890'
=== captures ===
Data: 15/06/1990 | Dia: 15 | Mês: 06 | Ano: 1990
Data: 23/02/2026 | Dia: 23 | Mês: 02 | Ano: 2026
=== replace ===
replace: Eu amo Ferrugem e uso Rust todo dia
replace_all: Eu amo Ferrugem e uso Ferrugem todo dia
BR->ISO: Data: 2026-02-23
Maiúsculas: OLÁ MUNDO RUST
=== split ===
Partes: ["maçã", "banana", "cereja", "damasco", "uva"]
=== Validação com LazyLock ===
'usuario@exemplo.com' válido? true
'invalido@' válido? false
'nome.sobrenome@empresa.com.br' válido? true
'@sem-nome.com' válido? false
=== Extrair dados ===
Hora: 2026-02-23 14:30:15 | Nível: INFO | Msg: Servidor iniciado na porta 8080
Hora: 2026-02-23 14:30:16 | Nível: WARN | Msg: Cache expirado, recarregando
Hora: 2026-02-23 14:30:17 | Nível: ERROR | Msg: Conexão recusada pelo banco
=== Flags ===
Case insensitive: ["Rust", "RUST", "rust", "RuSt"]
Multiline: ["123", "456", "789"]
Dicas de Performance
- Compile a regex uma vez: use
LazyLock<Regex>para regexes usadas repetidamente. Compilar regex é custoso. - A crate
regexgarante tempo linear: não há risco de backtracking exponencial como em outras linguagens. - Use
is_match()quando só precisa saber se combina — é mais rápido quecaptures(). - Prefira
find_iter()acaptures_iter()quando não precisa de grupos.
Veja Também
- Validar Email em Rust — validação completa de email com regex e crate validator
- Tratar Erros em Rust — como lidar com erros de compilação de regex
- Rust Cheatsheet — referência rápida de String e métodos de texto
- Tutorial: Primeiros Passos — fundamentos do Rust
- Glossário Rust — termos como Crate, Trait e Macro explicados
- Regex em Go — compare com o pacote regexp de Go