Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String concatenation in rust

Tags:

rust

I am trying to get a &str and &str to concatenate in a for loop withe intention of using the new combined string after a number of parts have been added to it. A general layout of the for loop can be seen below but I am having a lot of trouble combining strings due to numerous errors.

for line in reader.lines() {
    let split_line = line.unwrap().split(",");
    let mut edited_line = "";

    for word in split_line {
        if !word.contains("substring") {
            let test_string = [edited_line, word].join(",");
            edited_line = &test_string;
        }
    }
    let _ = writeln!(outfile, "{}", edited_line).expect("Unable to write to file"); 
}

First error:

error[E0716]: temporary value dropped while borrowed

Comes when running the above.

Second error:

error[E0308]: mismatched types expected &str, found struct std::string::String

happens when you remove the & from test_string when it is assigned to edited_line

Note: format! and concat! macros both also give error 2.
It seems to be if I get error 2 and convert the std::string:String and convert it to &str I get the error stating the variables don't live long enough.

How am I supposed to go about building a string of many parts?

like image 557
John7867 Avatar asked Jun 11 '26 20:06

John7867


1 Answers

Note that Rust has two string types, String and &str (actually, there are more, but that's irrelevant here).

  • String is an owned string and can grow and shrink dynamically.
  • &str is a borrowed string and is immutable.

Calling [edited_line, word].join(",") creates a new String, which is allocated on the heap. edited_line = &test_string then borrows the String and implicitly converts it to a &str.

The problem is that its memory is freed as soon as the owner (test_string) goes out of scope, but the borrow lives longer than test_string. This is fundamentally impossible in Rust, since it would otherwise be a use-after-free bug.

The correct and most efficient way to do this is to create an empty String outside of the loop and only append to it in the loop:

let mut edited_line = String::new();

for word in split_line {
    if !word.contains("substring") {
        edited_line.push(',');
        edited_line.push_str(word);
    }
}

Note that the resulting string will start with a comma, which might not be desired. To avoid it, you can write

let mut edited_line = String::new();

for word in split_line {
    if !word.contains("substring") {
        if !edited_line.is_empty() {
            edited_line.push(',');
        }
        edited_line.push_str(word);
    }
}

This could be done more elegantly with the itertools crate, which provides a join method for iterators:

use itertools::Itertools;

let edited_line: String = line
    .unwrap()
    .split(",")
    .filter(|word| !word.contains("substring"))
    .join(",");
like image 128
Aloso Avatar answered Jun 13 '26 19:06

Aloso