Reputation: 486
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
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
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
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