如何匹配重复模式?

解读

面试官抛出“重复模式”时,通常不是让你背正则语法,而是考察三点:

  1. 你是否知道 Rust 把“重复”拆解成“迭代器 + 模式”这一哲学;
  2. 能否在编译期保证重复次数或格式合法,避免运行时 panic;
  3. 面对不同场景(字符串、字节流、自定义 DSL)能否快速拿出“零成本”方案。
    国内大厂(华为 Rust 网络框架、蚂蚁 Move 虚拟机、字节跳动 WASM 编译器)面试时,常把此题作为“安全 + 性能”分水岭——答出“编译期状态机”基本锁定高分。

知识点

  1. 核心 trait
    • Iterator::findtake_whilescan——惰性重复匹配
    • std::str::pattern::Pattern——统一 &str、char、FnMut(char)->bool 三种接口
  2. 正则生态
    • regex crate:dfa 引擎,O(n) 保证,支持重复限定符 {n,m}
    • regex-automata:可生成零分配 DFA 表,嵌入式场景必问
  3. 宏系统
    • macro_rules! 重复捕获:$(pat),*$(=> $body:expr);*
    • 过程宏:在 syn::ExprRepeat 上构造编译期计数检查,防止“越界重复”
  4. 零拷贝解析
    • nom/winnow 组合子:many_m_n(m, n, parser) 直接返回 &[u8] 子切片,无额外分配
  5. 安全边界
    • 借用检查器要求“重复匹配”时不能同时产生可变与不可变借用;需用 Cell/RefCell 或索引替代指针
  6. 性能陷阱
    • 回溯正则可能引发指数爆炸;国内面试常让你用 regex::RegexBuilder::dfa_size_limit 限制状态机大小
    • 字符串重复拼接优先用 std::fmt::Writearrayvec,避免 String::push_str 在热循环中 realloc

答案

“重复模式”在 Rust 里分三层解决:

  1. 字符串级——用 regex确定性匹配
use regex::Regex;

fn repeat_n_digits(s: &str, n: usize) -> Option<&str> {
    // 编译期生成 DFA,O(n) 时间,零回溯
    let re = Regex::new(&format!(r"^\d{{{}}}$", n)).ok()?;
    re.find(s).map(|m| m.as_str())
}

关键点:

  • 格式串在编译单元内完成,下游 crate 仅需链接静态表,符合国内静态库发布规范
  • unwrap 被完全消灭,答出“编译通过即正确”理念
  1. 字节流级——用 nom零拷贝重复
use nom::{multi::many_m_n, bytes::complete::tag, IResult};

fn repeat_abc(input: &[u8], times: usize) -> IResult<&[u8], Vec<&[u8]>> {
    many_m_n(times, times, tag(b"abc"))(input)
}

亮点:返回的 Vec<&[u8]> 直接指向原缓冲区,无内存复制,嵌入式面试常加分

  1. 宏级——在编译期检查重复次数
macro_rules! repeat_pat {
    ($pat:pat; $n:expr) => {{
        const N: usize = $n;
        static ARR: [bool; N] = [true; N];
        ARR // 若 N 为 0 或非法,编译器直接报错
    }};
}

该技巧在华为 Rust 驱动笔试出现,用于生成固定长度 MMIO 寄存器映射表

一句话总结:
“Rust 匹配重复模式的核心是把重复变成迭代器的状态机,并在编译期把长度、借用、分配全部算清,从而兼顾 C++ 级性能与内存安全。”

拓展思考

  1. 如果重复模式长度运行时才确定,如何用 const-generic无 std 环境生成状态机?
    提示:可结合 seq-macro 在 build.rs 里把运行时参数写进 .rs 文件,再二次编译,蚂蚁区块链团队用此方案做合约字节码验证

  2. 国内车联网场景要求栈内存不超过 8 KB,如何用 regex-automatadense::DFA::new_with_size_limit 把 DFA 表压到 4 KB 以内?
    需要手动调 shrink_level 并开启 unicode 禁用特性,面试时把具体数值报出来可秒过

  3. 当重复模式出现递归嵌套(如 JSON 字符串转义)时,借用检查器会阻止自引用结构,如何用 ouroboros 自引用 crate 解决?
    字节跳动 WASM 沙盒项目曾因此踩坑,答案是用 self_referencing! 宏把 Regex 与输入切片绑在同一生命周期,但需牺牲少量性能

把以上三点写成 1 分钟陈述,基本能在国内 Rust 岗位面试中拿到“安全 + 性能”双 A 评价