Benjamin Peinhardt
Benjamin Peinhardt

Reputation: 486

How do I borrow a String as mutable to push itself to itself?

I am trying to solve a problem from r/dailyprogrammer and have run into a problem with borrowing a String as mutable.

I'm trying to see if a String has been "looped" around. Comparing "hello" to "llohe" would return true whereas "hello" to "oelhl" would not. My strategy was to double one string and see if it contains the other one:

fn main() {
    let x = String::from("hello");
    let mut y = String::from("llohe");
    println!("{}", compare(x, &mut y));
}

fn compare(x: String, y: &mut String) -> bool {
    y.push_str(y.as_str());
    if y.contains(&x) {
        true
    } else {
        false
    }
}
error[E0502]: cannot borrow `*y` as mutable because it is also borrowed as immutable
 --> src/main.rs:8:5
  |
8 |     y.push_str(y.as_str());
  |     ^^--------^-^^^^^^^^^^
  |     | |        |
  |     | |        immutable borrow occurs here
  |     | immutable borrow later used by call
  |     mutable borrow occurs here

I don't know where the heck I'm doing that. I am trying to become friends with the compiler, but it is quite prickly.

Upvotes: 3

Views: 859

Answers (3)

Ibraheem Ahmed
Ibraheem Ahmed

Reputation: 13518

It is not possible to borrow a String as mutable to push itself to itself. Instead, you have to first clone the string, and then push the cloned version to itself:

y.push_str(y.clone().as_str());

However, in your case you do not need to pass a mutable reference to y, because you are not modifying it. Instead, you can create a new string with String::repeat():

fn main() {
    let x = String::from("hello");
    let y = String::from("llohe");
    println!("{}", compare(x, y));
}

fn compare(x: String, y: String) -> bool {
    y.repeat(2).contains(&x)
}

Upvotes: 3

Shepmaster
Shepmaster

Reputation: 430368

As an answer to the problem instead of the question, you don't need heap allocation at all; iterators provide enough capability here:

fn main() {
    let x = "hello";
    let y = "llehe";
    println!("{}", compare(x, y));
}

fn compare(x: &str, y: &str) -> bool {
    let x = x.as_bytes();
    let mut y = y.as_bytes().iter().chain(y.as_bytes());

    loop {
        let yy = y.clone();
        if x.iter().eq(yy.take(x.len())) {
            return true; // or break true;
        }
        if y.next().is_none() {
            return false; // or break false;
        }
    }
}

Upvotes: 3

tadman
tadman

Reputation: 211540

There's no reason to have a mut ref at all, you can skip all that:

fn main() {
    let x = String::from("hello");
    let y = String::from("llohe");
    println!("{}", compare(&x, &y));
}

fn compare(x: &String, y: &String) -> bool {
    let mut z = y.clone();
    
    z.push_str(y.as_str());
    
    // You don't need to wrap a boolean in an if and return booleans,
    // just let the result of the test be the return value
    z.contains(x)
}

The as_str() function needs a reference to the string, but as you're holding a mutable reference that violates the borrow checker rules. Rust just isn't comfortable with all those references co-existing.

This is likely because you're asking to append to a string a copy of the string you're in the process of appending to, which could lead to problems. as_str needs the string to be stable, push_str needs to be able to change the string. They can't inter-operate like that.

You can side-step this by cloning the string:

fn main() {
    let x = String::from("hello");
    let mut y = String::from("llohe");
    println!("{}", compare(x, &mut y));
}

fn compare(x: String, y: &mut String) -> bool {
    y.push_str(y.clone().as_str());
    if y.contains(&x) {
        true
    } else {
        false
    }
}

Upvotes: 2

Related Questions