Reputation: 2705
I understand that the String
is dropped when the scope of the loop
ends and that the vector input
contains slices of trimmed_text
.
I suppose the resolution is to move the ownership of those slices to input
or something like that. How can this be done?
use std::io;
fn main() {
let mut input: Vec<&str>;
loop {
let mut input_text = String::new();
println!("Type instruction in the format Add <name> to <department>:");
io::stdin()
.read_line(&mut input_text)
.expect("failed to read from stdin");
let trimmed_text: String = input_text.trim().to_string();
input = trimmed_text.split(" ").collect();
if input[0] == "Add" && input[2] == "to" {
break;
} else {
println!("Invalid format.");
}
}
println!("{:?}", input);
}
The compile error:
error[E0597]: `trimmed_text` does not live long enough
--> src/main.rs:14:17
|
14 | input = trimmed_text.split(" ").collect();
| ^^^^^^^^^^^^ borrowed value does not live long enough
...
21 | }
| - `trimmed_text` dropped here while still borrowed
22 |
23 | println!("{:?}", input);
| ----- borrow later used here
Upvotes: 4
Views: 8283
Reputation: 1181
As you did understand, the problem is that the owner of the slices doesn't live long enough. What you didn't point out (and maybe did not explicitly see) is that the owner is the variable trimmed_text
. So what you want to do (if you don't want to copy each slice to have better performances) is to make trimmed_text
's scope bigger:
use std::io;
fn main() {
let mut input: Vec<&str>;
let mut trimmed_text: String;
loop {
...
trimmed_text = input_text.trim().to_string();
input = trimmed_text.split(" ").collect();
if input[0] == "Add" && input[2] == "to" {
break;
} else {
...
}
}
println!("{:?}", input);
}
Here, we resolved the error of the lifetime of the owner of the slices. However, we have a second issue:
13 | trimmed_text = input_text.trim().to_string();
| ^^^^^^^^^^^^ assignment to borrowed `trimmed_text` occurs here
14 |
15 | input = trimmed_text.split(" ").collect();
| ----- ------------ borrow of `trimmed_text` occurs here
| |
| borrow might be used here, when `input` is dropped and runs the `Drop` code for type `std::vec::Vec`
This tells us that after a loop, the input
variable still borrows trimmed_text
as it is modified. To fix this, we can reduce the scope of input so that input's scope doesn't contain the line 13 :
use std::io;
fn main() {
// Remove the "let mut input: Vec<&str>;"
let mut trimmed_text: String;
loop {
...
trimmed_text = input_text.trim().to_string();
let input: Vec<&str> = trimmed_text.split(" ").collect();
...
}
}
If we don't use input
outside of the loop, this works just fine. Now we just need to output the value of input
at the end of the loop :
use std::io;
fn main() {
// Remove the "let mut input: Vec<&str>;"
let mut trimmed_text: String;
let results = loop {
...
trimmed_text = input_text.trim().to_string();
let input: Vec<&str> = trimmed_text.split(" ").collect();
if input[0] == "Add" && input[2] == "to" {
break input;
}
...
};
println!("{:?}", results);
}
And here we have it !
Here is a code that does what you want without duplicating the slices :
use std::io;
fn main() {
let mut trimmed_text: String;
let results = loop {
let mut input_text = String::new();
println!("Type instruction in the format Add <name> to <department>:");
io::stdin()
.read_line(&mut input_text)
.expect("failed to read from stdin");
trimmed_text = input_text.trim().to_string();
let input: Vec<&str> = trimmed_text.split(" ").collect();
if input[0] == "Add" && input[2] == "to" {
break input;
} else {
println!("Invalid format.");
}
};
println!("{:?}", results);
}
I renamed input
to results
to avoid confusion with the input
variable inside the loop. Feel free to rename it back to input
if you really want to.
Upvotes: 1
Reputation: 15105
.split()
returns references to a String
which is dropped by the end of the loop, but you want input
to live past the end of the loop, so you should refactor it to hold owned values instead of references. Example:
use std::io;
fn example() {
let mut input: Vec<String>; // changed from &str to String
loop {
let mut input_text = String::new();
println!("Type instruction in the format Add <name> to <department>:");
io::stdin()
.read_line(&mut input_text)
.expect("failed to read from stdin");
// map str refs into owned Strings
input = input_text.trim().split(" ").map(String::from).collect();
if input[0] == "Add" && input[2] == "to" {
break;
} else {
println!("Invalid format.");
}
}
println!("{:?}", input);
}
Upvotes: 9