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?
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(",");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With