Error reading from BufReader in nested loops - rust

I am been working on a program in Rust to read individual lines from a local text file, divide the line into a character vector and do some other operations. This is the snippet of my code:
use std::io::{BufRead, BufReader};
use std::fs::File;
fn main() {
// ----<code snippet>-----
let input_file = File::open("--<text file location>--").unwrap();
let input_file = BufReader::new(input_file);
let mut temp2 = String::new();
let mut counter = 0;
while counter < 12 {
for line in input_file.lines() {
let input_line = line.expect("Failed to read line");
let temp: Vec<char> = input_line.chars().collect::<Vec<_>>();
temp2.push(temp[counter]);
}
//-----<program continues without any issues>-------
Here is the error message as shown up in Cargo:
error[E0382]: use of moved value: `input_file`
--> src\main.rs:48:21
|
42 | let input_file = BufReader::new(input_file);
| ---------- move occurs because `input_file` has type `BufReader<File>`, which does not implement the `Copy` trait
...
48 | for line in input_file.lines() {
| ^^^^^^^^^^ ------- `input_file` moved due to this method call, in previous iteration of loop
|
note: this function takes ownership of the receiver `self`, which moves `input_file`
--> C:\Users\Gumbi\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\std\src\io\mod.rs:2258:14
|
2258 | fn lines(self) -> Lines<Self>
| ^^^^
error[E0382]: borrow of moved value: `temp2`
--> src\main.rs:51:13
|
44 | let mut temp2 = String::new();
| --------- move occurs because `temp2` has type `String`, which does not implement the `Copy` trait
...
51 | temp2.push(temp[counter]);
| ^^^^^ value borrowed here after move
...
54 | let temp3 = most_common_digit(temp2);
| ----- value moved here, in previous iteration of loop
I know the basics of ownership and borrowing, but I am not able to grasp what is the problem in this code. Could anyone please help me in knowing where I am going wrong?

The problem is that input_file is moved inside the while loop scope. Instead use an intermediary &mut that can be dropped in each iteration:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
// ----<code snippet>-----
let input_file = File::open("--<text file location>--").unwrap();
let mut input_file = BufReader::new(input_file);
let mut temp2 = String::new();
let mut counter = 0;
while counter < 12 {
let mut input_file_ref = &mut input_file;
for line in input_file_ref.lines() {
let input_line = line.expect("Failed to read line");
let temp: Vec<char> = input_line.chars().collect::<Vec<_>>();
temp2.push(temp[counter]);
}
}
}
Playground

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;
}
}
}

Peek iterator inside `for` loop

I'm trying to peek at the char in-front of my current location whilst iterating over a &str.
let myStr = "12345";
let mut iter = myStr.chars().peekable();
for c in iter {
let current: char = c;
let next: char = *iter.peek().unwrap_or(&'∅');
}
I will be passing this char into a method down the line. However, even this MRE produces a borrow after move error that I'm not sure how to get past.
error[E0382]: borrow of moved value: `iter`
--> src/lib.rs:7:27
|
4 | let mut iter = myStr.chars().peekable();
| -------- move occurs because `iter` has type `Peekable<Chars<'_>>`, which does not implement the `Copy` trait
5 | for c in iter {
| ---- `iter` moved due to this implicit call to `.into_iter()`
6 | let current: char = c;
7 | let next: char = *iter.peek().unwrap_or(&'∅');
| ^^^^^^^^^^^ value borrowed here after move
|
note: this function takes ownership of the receiver `self`, which moves `iter`
--> /home/james/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:267:18
|
267 | fn into_iter(self) -> Self::IntoIter;
Any idea what's going on here? I've tried various combinations of referencing and dereferencing but nothing I've tried seems to work.
The iterator is moved into the for loop. You cannot manually manipulate an iterator inside a for loop. However, the for loop can be replaced by while let:
while let Some(c) = iter.next() {
let current: char = c;
let next: char = *iter.peek().unwrap_or(&'∅');
}
Playground.
If you can work with slices, it will get much easier with windows():
let slice = ['1', '2', '3', '4', '5'];
let iter = slice.windows(2);
for arr in iter {
let current = arr[0];
let next = arr[1];
}
Playground

Trimming lines of input for a set in Rust [duplicate]

This question already has answers here:
How to iterate and extract values out of a for loop in Rust
(2 answers)
error[E0597]: borrowed value does not live long enough in While loop
(2 answers)
Return local String as a slice (&str)
(7 answers)
Closed 2 years ago.
This Rust program collects words/lines from the user, and adds each to the variable line_set. I'd like to change the code to trim each word before adding it to line_set.
use std::collections::HashSet;
use std::io;
fn main() {
let mut line_set = HashSet::new();
for i in 1..4 {
let mut line = String::new();
io::stdin()
.read_line(&mut line)
.expect("Failed to read line");
//let line = line.trim();
line_set.insert(line.clone());
if i == 3 {
for l in &line_set {
println!("{}", l);
}
}
}
}
When I try to add a call to String::trim, applied to the current word, the program no longer compiles:
error[E0597]: `line` does not live long enough
--> src/main.rs:12:20
|
12 | let line = line.trim();
| ^^^^ borrowed value does not live long enough
13 | line_set.insert(line.clone());
| -------- borrow later used here
...
19 | }
| - `line` dropped here while still borrowed
I used rustc's --explain switch, and it relates that "This error occurs because a value was dropped while it was still borrowed". I had hoped using the clone method would avoid that problem. How do I get past the error?
str::trim just produces a slice, not another String, so when you call clone on it, you're calling &str's implementation of Clone, which just copies the &str (a cheap pointer copy). Instead, you should use one of the methods to turn the &str into a String, such as to_string, to_owned or more verbosely, String::from.
use std::collections::HashSet;
use std::io;
fn main() {
let mut line_set = HashSet::new();
for i in 1..4 {
let mut line = String::new();
io::stdin()
.read_line(&mut line)
.expect("Failed to read line");
line_set.insert(line.trim().to_owned());
if i == 3 {
for l in &line_set {
println!("{}", l);
}
}
}
}
(playground)

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.

How to increase lifetime of the bindings [duplicate]

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"]

Resources