How to increase lifetime of the bindings [duplicate] - rust

This question already has answers here:
Extending borrowed lifetime for String slice
(2 answers)
Closed 5 years ago.
I have the following code ...
use std::process::Command;
fn main() {
let cmds = vec![vec!["ls", "-lh"], vec!["grep", "foo"]];
let mut processes: Vec<&mut Command> = Vec::new();
let mut i = 0;
let length = cmds.len();
while i < length {
let cmd = cmds[i].clone();
let mut p = Command::new(&cmd[0]).args(&(cmd[1..]));
processes.push(p);
i += 1;
}
println!("processes: {:?}", processes);
// want to manipulate processes elements here again.
// ...
}
Which doesn't compile:
error: borrowed value does not live long enough
--> src/main.rs:11:60
|
11 | let mut p = Command::new(&cmd[0]).args(&(cmd[1..]));
| --------------------- ^ temporary value dropped here while still borrowed
| |
| temporary value created here
...
19 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
I understand why it refused to compile, I just don't know how to fix it in this case.

Instead of storing a borrowed reference, you could store the Command object itself.
let cmds = vec![vec!["ls", "-lh"], vec!["grep", "foo"]];
let mut processes: Vec<Command> = Vec::new();
// ^ you can store concrete objects instead of references.
for cmd in &cmds {
// ^ prefer a `for` loop over a `while` loop. (Also, you don't need to clone the cmds)
let mut p = Command::new(cmd[0]);
p.args(&cmd[1..]);
// ^ you can get a concrete `Command` instead of `&mut Command` reference
// if you store the returned object from `new()`.
processes.push(p);
}
println!("processes: {:?}", processes);
// processes: ["ls" "-lh", "grep" "foo"]

Related

Read file with BufReader line by line and put in HashMap error borrowed value does not live long enough

I would like to read a file line by line and then process the words. I use HashMap and the entry API for that. However I get a 'borrowed value does not live long enough' error and am puzzled how to fix this.
1 use std::fs::File;
2 use std::io::{BufRead, BufReader};
3 use std::collections::HashMap;
4
5 fn main() {
6
7 let mut wmap: HashMap<_, i32> = HashMap::new();
8 let file = File::open("book1.txt").unwrap();
9 let reader = BufReader::new(file);
10 for (_index, line) in reader.lines().enumerate() {
11 let line = line.unwrap(); // Ignore errors.
12 let words = line.split_whitespace();
13 for word in words {
14 println!("{}.:.{}", _index, word);
15 *wmap.entry(word).or_insert(0) += 1;
16 }
17 }
18
19 }
The error I get is
error[E0597]: `line` does not live long enough
--> example-words.rs:12:17
|
12 | let words = line.split_whitespace();
| ^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
15 | *wmap.entry(word).or_insert(0) += 1;
| ---------------- borrow later used here
16 | }
17 | }
| - `line` dropped here while still borrowed
error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`.
I am aware that this is very similar to Borrowed Value Using BufReader and Lines in Extra Function. However I tried to do it all in one main function whereas the other example uses the extra function
read_lines(filename: &str) -> Result<Lines<BufReader<File>>, Error>
Thanks for any help
You are passing a borrowed string slice (&str) to a HashMap that "lives longer" than the borrowed value word. For this to work the borrowed value would need to have the same lifetime as your HashMap OR the HashMap needs to have ownership of the value inside of word. Here's an example:
use std::io;
use std::collections::HashMap;
fn main() {
let mut db = HashMap::new(); //initialize mutable hashmap outside of the loop
loop{
//I start a loop to take in multiple key val arguments from the
//command line but this means each iteration of the loop will
//clean up heap variables and any &str borrowing from these
//variables will be invalid after each iteration and the rust
// borrow checker will let us know if we are trying to access
// these invalid references, hence the compiler error
let mut string = String::new();
io::stdin().read_line(&mut string).unwrap();
let command: Vec<&str> = string.trim().split(" ").collect();
db.insert(command[0], command[1]);
}
}
I end up with the same compiler error:
error[E0597]: `string` does not live long enough
--> main.rs:9:30
|
9 | let command: Vec<&str> = string.trim().split(" ").coll...
| ^^^^^^^^^^^^^ borrowed value does not live long enough
10 | db.insert(command[0], command[1]);
| --------------------------------- borrow later used here
11 | }
| - `string` dropped here while still borrowed
This is because on every iteration of the loop the string slice I intend my HashMap to borrow gets dropped (goes out of scope and is no longer valid) and rust keeps us from having dangling references. Instead change the db.insert(command[0], command[1]) to db.insert(command[0].to_string(), command[2].to_string()). This will convert the &str -> String which will then be "owned" by the HashMap instance and survive for the remainder of the running
program. In your case:
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::collections::HashMap;
fn main() {
let mut wmap: HashMap<_, i32> = HashMap::new();
let file = File::open("book1.txt").unwrap();
let reader = BufReader::new(file);
for (_index, line) in reader.lines().enumerate() {
let line = line.unwrap(); // Ignore errors.
let words = line.split_whitespace();
for word in words {
println!("{}.:.{}", _index, word);
*wmap.entry(word.to_string()).or_insert(0) += 1;
}
}
}
this will compile and run :)
Hope that helps!
As commented by #cdhowie, you need to own the string using word.to_owned().
While it is not an error, Rust naming conventions say that an underscore in front of a variable implies that it is not used, so I renamed _index to index as well.
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let mut wmap: HashMap<_, i32> = HashMap::new();
let file = File::open("book1.txt").unwrap();
let reader = BufReader::new(file);
for (index, line) in reader.lines().enumerate() {
let line = line.unwrap(); // Ignore errors.
let words = line.split_whitespace();
for word in words {
println!("{}.:.{}", index, word);
*wmap.entry(word.to_owned()).or_insert(0) += 1;
}
}
}

cannot borrow `hsets` as mutable because it is also borrowed as immutable [duplicate]

This question already has answers here:
How can I borrow from a HashMap to read and write at the same time?
(2 answers)
Closed 2 years ago.
I want move elements of HashSet[0] to HashSet[1], but always encounter borrowed error:
I try using a tmp vec to save the elemetns, but problem still exists:
use std::collections::HashSet;
fn main() {
let mut hsets = vec![];
hsets.push(HashSet::new());
hsets[0].insert("a1");
hsets[0].insert("a2");
hsets.push(HashSet::new());
hsets[1].insert("b1");
hsets[1].insert("b2");
// tmp vec save hsets[0]: [a1, a2]
let mut arr = vec![];
for v in &hsets[0] {
arr.push(v);
}
for v2 in arr {
hsets[1].insert(v2);
}
}
Result:
error[E0502]: cannot borrow `hsets` as mutable because it is also borrowed as immutable
--> src/main.rs:18:9
|
13 | for v in &hsets[0] {
| ----- immutable borrow occurs here
...
17 | for v2 in arr {
| --- immutable borrow later used here
18 | hsets[1].insert(v2);
| ^^^^^ mutable borrow occurs here
error: aborting due to previous error
I'm assuming you don't want to move the HashSets out of the Vec or de-allocate them, in which case you can do this:
use std::collections::HashSet;
fn main() {
let mut hsets = vec![];
// first set
hsets.push(HashSet::new());
hsets[0].insert("a1");
hsets[0].insert("a2");
// second set
hsets.push(HashSet::new());
hsets[1].insert("b1");
hsets[1].insert("b2");
dbg!(&hsets);
assert_eq!(hsets[0].len(), 2);
assert_eq!(hsets[1].len(), 2);
// move elements from first set to second set
let (first, second) = hsets.split_at_mut(1);
second[0].extend(first[0].drain());
dbg!(&hsets);
assert_eq!(hsets[0].len(), 0);
assert_eq!(hsets[1].len(), 4);
}
playground
If you'd like to get a deeper understanding of why your code as-is doesn't compile then read How to get mutable references to two array elements at the same time?.
Try to be explicit about the type of the Vector like
let mut arr: Vec<&str> = vec![]; // otherwise compiler is interpreting as Vec<&&str>
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d0b4e82ab1eb12791a18cf6d33ad0ca4

"cannot borrow as mutable because it is also borrowed as immutable" with match [duplicate]

This question already has answers here:
How to lookup from and insert into a HashMap efficiently?
(2 answers)
Closed 3 years ago.
I'm attempting to count the character frequency in a string and store the count of each character in a BTreeMap. However, I'm getting a warning and would like to get rid of it.
This is what I've tried:
use std::collections::BTreeMap;
fn letter_frequency(input: &str) -> BTreeMap<char, i32> {
let mut tree: BTreeMap<char, i32> = BTreeMap::new();
for item in &input.chars().collect::<Vec<char>>() {
match tree.get(item) {
Some(count) => tree.insert(*item, *count + 1),
None => tree.insert(*item, 1)
};
}
tree
}
This is the warning:
warning: cannot borrow `tree` as mutable because it is also borrowed as immutable
--> src/lib.rs:7:28
|
6 | match tree.get(item) {
| ---- immutable borrow occurs here
7 | Some(count) => tree.insert(*item, *count + 1),
| ^^^^ ------ immutable borrow later used here
| |
| mutable borrow occurs here
|
= note: #[warn(mutable_borrow_reservation_conflict)] on by default
= warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
= note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
How do I correctly use match with a BTreeMap to avoid the error ?
Like Svetlin mentions in a comment, the entry API is your friend. Below I've also removed one unneeded collect.
fn letter_frequency(input: &str) -> BTreeMap<char, i32> {
let mut tree = BTreeMap::new();
for item in input.chars() {
let count = tree.entry(item).or_insert(0);
*count += 1;
}
tree
}
The temporary variable count is not really needed: *tree.entry(item).or_insert(0) += 1; works just fine but might look a bit crowded at first.

Cannot assign to `x` because it is borrowed

Getting the below error when trying to push more elements to a vector. How to overcome this error
error[E0506]: cannot assign to `x` because it is borrowed
x = format!(r"\*START TIME*{:?}\S*\s+(?P<Value>[a-zA-Z0-9_\-\.]+)", ts);
| ^^ assignment to borrowed `x` occurs here
76 | core_regex_dict.push(&x);
| --------------- --- borrow of `x` occurs here
| |
| borrow later used here
Code:
let mut x = String::new();
for ts in test_list {
x = format!(r"\*START TIME*{:?}\S*\s+(?P<Value>[a-zA-Z0-9_\-\.]+)", ts);
core_regex_dict.push(&x);
}
It's hard to answer your question because you didn't give us enough information. In particular, we need to know how core_regex_dict is defined. I'm assuming that core_regex_dict has type Vec<&str>. You need to change that to Vec<String>:
let core_regex_dict = Vec::new(); // No need to specify the item type, it will be inferred.
// No need to declare `x` before the loop unless you need to use the
// last value afterward...
for ts in test_list {
let x = format!(r"\*START TIME*{:?}\S*\s+(?P<Value>[a-zA-Z0-9_\-\.]+)", ts);
core_regex_dict.push (x); // No `&` -> we push the String itself instead of a reference
}
Found a solution that involves leaking the memory of the String in https://stackoverflow.com/a/30527289/12323498
But, is there a better way to achieve this without leaking the memory?
fn string_to_static_str(s: String) -> &'static str {
Box::leak(s.into_boxed_str())
}
and the code look like this now
let mut s = String::new();
for ts in test_list {
s = format!(r"\*START TIME*{:?}\S*\s+(?P<Value>[a-zA-Z0-9_\-\.]+)", ts);
let s: &'static str = string_to_static_str(s);
core_regex_dict.push(s);
}

How can I solve "use of moved value" and "which does not implement the `Copy` trait"?

I'm trying to read the values from a vector and use the values as indexes to perform an addition:
fn main() {
let objetive = 3126.59;
// 27
let values: Vec<f64> = vec![
2817.42, 2162.17, 3756.57, 2817.42, -2817.42, 946.9, 2817.42, 964.42, 795.43, 3756.57,
139.34, 903.58, -3756.57, 939.14, 828.04, 1120.04, 604.03, 3354.74, 2748.06, 1470.8,
4695.71, 71.11, 2391.48, 331.29, 1214.69, 863.52, 7810.01,
];
let values_number = values.len();
let values_index_max = values_number - 1;
let mut additions: Vec<usize> = vec![0];
println!("{:?}", values_number);
while additions.len() > 0 {
let mut addition: f64 = 0.0;
let mut saltar: i32 = 0;
// Sumar valores en additions
for element_index in additions {
let addition_aux = values[element_index];
addition = addition_aux + addition;
}
}
}
I get the following error. How can I solve it?
error[E0382]: use of moved value: `additions`
--> src/main.rs:18:11
|
18 | while additions.len() > 0 {
| ^^^^^^^^^ value used here after move
...
23 | for element_index in additions {
| --------- value moved here
|
= note: move occurs because `additions` has type `std::vec::Vec<usize>`, which does not implement the `Copy` trait
error[E0382]: use of moved value: `additions`
--> src/main.rs:23:30
|
23 | for element_index in additions {
| ^^^^^^^^^ value moved here in previous iteration of loop
|
= note: move occurs because `additions` has type `std::vec::Vec<usize>`, which does not implement the `Copy` trait
The fix for this particular problem is to borrow the Vec you're iterating over instead of moving it:
for element_index in &additions {
let addition_aux = values[*element_index];
addition = addition_aux + addition;
}
but your code has other problems. You never change additions by adding or removing elements, so your while additions.len() > 0 will never terminate. I hope this is because you haven't finished and wanted to work out how to fix the immediate problem before writing the rest of the function.
For now, you might benefit from re-reading the chapter of the Rust Book about ownership, moves, and borrowing.

Resources