Rust lifetime issue on join two &str inside loop

Hello

I have a problem with some basic Rust code, currently want to achieve something like this

let mut strings= vec!["aaa", "bb", "cc"];
for string in &mut strings{
    *string = string + "meteor";
}
println!("{:?}", strings)

have tried to change

*string = &(string.to_owned() + "meteor");

and then got lifetime temporary issues.

You're mistaken on types.

  1. Read the error:
error[E0369]: cannot add `&str` to `&mut &str`
 --> src/main.rs:4:18
  |
4 | *string = string + "meteor";
  |           ------ ^ -------- &str
  |           |
  |           &mut &str
  1. Find impls of Add trait on &str:
impl Add<&str> for String {
    type Output = String;
    fn add(self, other: &str) -> String { ... }
}

It means String + &str = String.

  1. So you can find a solution:
let strs = vec!["aaa", "bb", "cc"];
let mut strings = vec![];
for s in &strs {
    strings.push(s.to_string() + "meteor");
}

Update: There are several ways to concatenate &str. It's more common to use format!, since the macro handles references for you[1].

-    strings.push(s.to_string() + "meteor");
+    strings.push(format!("{s}meteor"));

  1. You might be surprised when using strings.push(s.to_ownd() + "meteor"); which fails. ↩︎

1 Like

To explain the lifetime issue: It’s an ownership issue! The variable strings of type Vec<&str> contains borrowed strings. Initially, the string literals "aaa", "bb", "cc" borrow from static program data, so they don’t need a visible owner. But once you start concatenating, there are new string values generated at run-time.

New string values (e.g. as opposed to borrows or slices of existing strings) require some memory to be written into, and some owner for that memory. string.to_owned() allocates this memory, and the + "meteor" modifies it, but the strings variable of type Vec<&str> is unable to own any string data! Hence, the borrow &(string.to_owned() + "meteor") borrows data owned by nobody – beyond the implicit temporary variable that will own it for the duration of that statement – which leads to a borrow-checking error when you try to keep this borrow alive for too long.

@vague already proposed approaches for creating a new vector, one that can own its string data, of type Vec<String>. If you want to modify an existing vector instead, you would need to start out with a Vec<String>, e.g. let mut strings= vec!["aaa".to_owned(), "bb".to_owned(), "cc".to_owned()];, then you can use e.g. *string += "meteor" in a loop without problem.

4 Likes

Hi, Thank you for the very detailed explanation, it's really helpful for me to understand the issue