如何修改在循环中使用自身的 Cow 变量?

How to modify a Cow variable that uses itself in a loop?

我正在尝试删除字符串中的所有括号。没有考虑太难,我只是做了一个简单的正则表达式替换(即问题不是特别是关于摆脱任意级别的嵌套括号,但如果你愿意,请随时在评论中提出更好的方法).

use regex::Regex;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let input = "Text (with some (nested) parentheses)!";
    let re = Regex::new(r"\([^()]*\)")?;

    let output = re.replace_all(&input, "");
    let output = re.replace_all(&output, "");
    //    let output = re.replace_all(&output, "");
    //    let output = re.replace_all(&output, "");
    //    let output = re.replace_all(&output, "");
    //    let output = re.replace_all(&output, "");
    // ...

    assert_eq!("Text !", output);

    println!("Works!");

    Ok(())
}

因为我不知道括号是如何嵌套的,所以我需要循环替换而不是重复它"just enough times"。但是,创建一个循环会创建一个新的范围,这就是我在与借用检查器的讨论中遇到的死点。

显示我在循环中尝试做的事情的最简单的情况是:

    let mut output = re.replace_all(&input, "");
    while re.is_match(&output) {
        output = re.replace_all(&output, "");
    }

但是这无法完成,因为我正在分配给借用的变量:

error[E0506]: cannot assign to `output` because it is borrowed
 --> src/main.rs:9:9
  |
9 |         output = re.replace_all(&output, "");
  |         ^^^^^^                  ------- borrow of `output` occurs here
  |         |
  |         assignment to borrowed `output` occurs here
  |         borrow later used here

理想情况下,我想做的是创建具有相同名称的新变量绑定,但使用 let output = 会影响外部变量绑定,因此循环将无限循环。

无论我创建什么内部或外部临时变量,我都不能让它做我想做的事。我还尝试使用 re.replace_all() returns Cow 这一事实,并尝试在几个地方使用 .to_owned().to_string(),但这也无济于事。

这里是 link to a playground

re.replace_all() returns Cow

这就是问题的根源。编译器知道 return 值可能引用 output,但它也会 替换 output,导致 output 被删除离开。如果它允许这样做,引用将指向未分配的内存,导致内存不安全。

解决办法是完全避免借用。

tried using .to_owned()

to_owned Cow 只是 return 相同 Cow。也许你的意思是 into_owned?

let mut output = re.replace_all(&input, "").into_owned();
while re.is_match(&output) {
    output = re.replace_all(&output, "").into_owned();
}

and .to_string() in a couple of places

这也有效:

let mut output = re.replace_all(&input, "").to_string();
while re.is_match(&output) {
    output = re.replace_all(&output, "").to_string();
}

Shepmaster 的答案有效,但效率不高。 Cow 类型的一个微妙 属性 是通过检查它,我们可以确定字符串是否被修改,如果没有则跳过工作。

由于 Rust 类型系统的限制,如果值 未修改 Cow::into_owned() 进行复制。 ( 修改后的 值的 Cow::into_owned() 不会复制)。 (into_owned documentation)

在您的用例中,我们可以检测未修改的 Cow -- Cow::Borrowed -- 并跳过 into_owned().

    let mut output = /* mutable String */;
    while re.is_match(&output).unwrap() {
        match re.replace_all(&output, "") {
            // Unmodified -- skip copy
            Cow::Borrowed(_) => {}
            // replace_all() returned a new value that we already own
            Cow::Owned(new) => output = new,
        }
    }

但我们可以更进一步。同时调用 is_match()replace_all() 意味着模式匹配了两次。凭借我们对 Cows 的新知识,我们可以优化它:

    let mut output = /* mutable String */;
    // Cow::Owned is returned when the string was modified.
    while let Cow::Owned(new) = re.replace_all(&output, "") {
        output = new;
    }

编辑:如果您的输入值是不可变的,您也可以通过将其设为 Cow 来避免 .to_string() 复制:

    let input = "value";
    let mut output = Cow::from(input);
    while let Cow::Owned(new) = re.replace_all(&output, "") {
        output = Cow::Owned(new);
    }