You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中避免重复实例化Regex?

解决Rust中Regex复用的所有权与借用问题(Advent of Code 2023 Day 1)

问题根源

你遇到的借用检查器报错,是因为Regex类型未实现Copy trait,直接传递Regex实例会触发所有权转移——第一次调用parse_line后,first_regex和second_regex的所有权会被移动到函数内部,后续迭代无法再使用原变量。下面是两种符合你需求的解决方案:


优先方案:通过引用传递复用Regex

这是性能最优的方案,仅传递Regex的不可变引用,全程保留原实例的所有权,实现完全复用。

修改后的代码示例:

use regex::Regex;
use std::fs;

fn parse_number(re: &Regex, line: &str) -> Option<i32> {
    // 示例:匹配第一个数字并解析
    re.find(line)
        .and_then(|m| m.as_str().parse().ok())
}

fn parse_line(first_regex: &Regex, second_regex: &Regex, line: &str) -> (i32, i32) {
    let first_parsed = parse_number(first_regex, line).unwrap();
    let last_parsed = parse_number(second_regex, line).unwrap_or(first_parsed);
    (first_parsed, last_parsed)
}

fn day_1(path: &str) -> i32 {
    // 仅编译一次正则表达式
    let first_regex = Regex::new(r"\d").unwrap(); // 匹配第一个数字
    let second_regex = Regex::new(r"\d(?=[^\d]*$)").unwrap(); // 匹配最后一个数字
    
    let file = fs::read_to_string(path).expect("Failed to read input file");
    
    file.lines().fold(0, |acc, line| {
        let (first, last) = parse_line(&first_regex, &second_regex, line);
        acc + first * 10 + last
    })
}

核心改动:

  • 将parse_number和parse_line的参数从Regex改为&Regex,仅传递引用而非转移所有权
  • 在fold迭代中传递&first_regex和&second_regex,保证原实例可被重复借用

备选方案:克隆Regex实现复用

Regex的克隆是轻量级操作——内部仅复制指向已编译正则表达式的指针,不会重新编译,因此性能远优于每次重新初始化。

修改后的代码示例:

use regex::Regex;
use std::fs;

fn parse_number(re: Regex, line: &str) -> Option<i32> {
    re.find(line)
        .and_then(|m| m.as_str().parse().ok())
}

fn parse_line(first_regex: Regex, second_regex: Regex, line: &str) -> (i32, i32) {
    let first_parsed = parse_number(first_regex, line).unwrap();
    let last_parsed = parse_number(second_regex, line).unwrap_or(first_parsed);
    (first_parsed, last_parsed)
}

fn day_1(path: &str) -> i32 {
    let first_regex = Regex::new(r"\d").unwrap();
    let second_regex = Regex::new(r"\d(?=[^\d]*$)").unwrap();
    
    let file = fs::read_to_string(path).expect("Failed to read input file");
    
    file.lines().fold(0, |acc, line| {
        // 每次迭代克隆正则实例,避免所有权转移
        let (first, last) = parse_line(first_regex.clone(), second_regex.clone(), line);
        acc + first * 10 + last
    })
}

额外优化:全局缓存正则表达式

如果需要在多个函数中复用正则,可以用lazy_static或once_cell实现全局编译,避免手动传递引用:

use lazy_static::lazy_static;
use regex::Regex;
use std::fs;

lazy_static! {
    static ref FIRST_REGEX: Regex = Regex::new(r"\d").unwrap();
    static ref LAST_REGEX: Regex = Regex::new(r"\d(?=[^\d]*$)").unwrap();
}

fn parse_number(re: &Regex, line: &str) -> Option<i32> {
    re.find(line)
        .and_then(|m| m.as_str().parse().ok())
}

fn parse_line(line: &str) -> (i32, i32) {
    let first_parsed = parse_number(&FIRST_REGEX, line).unwrap();
    let last_parsed = parse_number(&LAST_REGEX, line).unwrap_or(first_parsed);
    (first_parsed, last_parsed)
}

fn day_1(path: &str) -> i32 {
    let file = fs::read_to_string(path).expect("Failed to read input file");
    
    file.lines().fold(0, |acc, line| {
        let (first, last) = parse_line(line);
        acc + first * 10 + last
    })
}

内容的提问来源于stack exchange,提问作者mzedeler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 13:38:10