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

时间:2019-04-13 22:47:30

标签: loops rust borrow-checker borrowing

我正在尝试删除字符串中的所有括号。不用太费劲,我只是做一个简单的regexp替换(即,问题不是特别要摆脱嵌套括号的任意级别,而是可以随意在注释中建议一种更好的方法) )。

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(())
}

因为我不知道括号的嵌套方式,所以我需要循环进行替换,而不是重复“足够多次”。但是,创建循环会创建一个新的作用域,而这正是我在借阅检查器的讨论中遇到的僵局。

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

    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()返回Cow的事实,并尝试在几个地方使用.to_owned().to_string(),但这都没有帮助。

这里是link to a playground

2 个答案:

答案 0 :(得分:1)

  

re.replace_all()返回Cow

这是问题的根源。编译器知道返回值可能引用output,但也会替换 output,导致output立即被丢弃。如果允许这样做,则引用将指向未分配的内存,从而导致内存不安全。

解决方案是完全避免借款。

  

使用.to_owned()

to_owned上的

Cow仅返回相同的Cow。也许您是说into_owned

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

.to_string()在几个地方

这同样有效:

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

很遗憾您没有展示出解决问题的实际尝试,我们本可以帮助您了解为什么它们不起作用。

答案 1 :(得分:0)

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

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

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

    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() 表示模式匹配两次。凭借我们对 Cow 的新知识,我们可以对其进行优化:

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