loopsrustborrow-checkerborrowing

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


I am trying to remove all the parentheses in a string. Not thinking about it too hard, I just do a simple regexp replace (i.e. the problem in question is not particularly about getting rid of arbitrary levels of nested parentheses, but feel free to suggest a better way of doing that in a comment if you want).

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

Because I do not know how nested the parentheses will be, I need to do the replacement in a loop rather than repeating it "just enough times". Creating a loop, however, creates a new scope and that's where I'm hitting a dead point in the discussion with the borrow checker.

The simplest case that shows what I am trying to do in the loop would be:

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

However that cannot be done because I am assigning to a borrowed variable:

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

What I would like to do, ideally, is to create new variable binding with the same name, but using let output = will shadow the outer variable binding, so the loop would cycle infinitely.

No matter what inner or outer temporary variable I create I cannot make it do what I want. I also tried using the fact that re.replace_all() returns Cow and tried using .to_owned() and .to_string() in a couple of places, but that didn't help either.

Here's a link to a playground.


Solution

  • re.replace_all() returns Cow

    This is the root of the problem. The compiler knows that the return value might reference output, but it will also replace output, causing output to be dropped right away. If it allowed this, the reference would point to unallocated memory, leading to memory unsafety.

    The solution is to avoid borrowing at all.

    tried using .to_owned()

    to_owned on a Cow just returns the same Cow. Perhaps you meant 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

    This works as well:

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