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

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.