I'm trying to read some lines from a file, skipping the first few and printing the rest, but I keep getting errors about used value after move:
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read};
use std::path::Path;
fn skip_and_print_file(skip: &usize, path: &Path) {
let mut skip: usize = *skip;
if let Ok(file) = File::open(path) {
let mut buffer = BufReader::new(file);
for (index, line) in buffer.lines().enumerate() {
if index >= skip {
break;
}
}
print_to_stdout(&mut buffer);
}
}
fn print_to_stdout(mut input: &mut Read) {
let mut stdout = io::stdout();
io::copy(&mut input, &mut stdout);
}
fn main() {}
This is the error I'm getting:
error[E0382]: use of moved value: `buffer`
--> src/main.rs:15:30
|
10 | for (index, line) in buffer.lines().enumerate() {
| ------ value moved here
...
15 | print_to_stdout(&mut buffer);
| ^^^^^^ value used here after move
|
= note: move occurs because `buffer` has type `std::io::BufReader<std::fs::File>`, which does not implement the `Copy` trait
In order to avoid the move, use the Read::by_ref() method. That way, you only borrow the BufReader:
for (index, line) in buffer.by_ref().lines().enumerate() { ... }
// ^^^^^^^^^
// you can still use `buffer` here
As Lukas Kalbertodt says, use Read::by_ref.
This prevents lines from consuming the BufReader and instead it consumes a &mut BufReader. The same logic applies to iterators.
Instead of implementing skip yourself, you can use Iterator::take. This has to be driven to completion with a for loop though:
use std::{
fs::File,
io::{self, BufRead, BufReader, Read},
path::Path,
};
fn skip_and_print_file(skip: usize, path: impl AsRef<Path>) {
if let Ok(file) = File::open(path) {
let mut buffer = BufReader::new(file);
for _ in buffer.by_ref().lines().take(skip) {}
// Or: buffer.by_ref().lines().take(skip).for_each(drop);
print_to_stdout(buffer);
}
}
fn print_to_stdout(mut input: impl Read) {
let mut stdout = io::stdout();
io::copy(&mut input, &mut stdout).expect("Unable to copy");
}
fn main() {
skip_and_print_file(2, "/etc/hosts");
}
Note that there's no reason to make the skip variable mutable or even to pass in a reference. You can also take in AsRef<Path> and then callers of skip_and_print_file can just pass in a string literal.
Related
A simple program, what I am trying to do is get lines from a file, or if the file is not present, pass in a zero-length iterator:
use std::fs::File;
use std::io::{self, BufRead};
fn run_against_input(inp: &mut dyn Iterator<Item = String>) {
for i in inp {
println!("Input line: {}", i);
}
}
fn main() {
let file = File::open("data.txt");
let input: dyn Iterator<Item = String> = match file {
Ok(f) => io::BufReader::new(f).lines()
.map(|line| line.unwrap()),
Err(_) => Vec::new().into_iter()
};
run_against_input(&mut dyn input);
}
When I do this, I get the following error:
Compiling playground v0.0.1 (/playground)
error: expected expression, found keyword `dyn`
--> src/main.rs:19:28
|
19 | run_against_input(&mut dyn input);
| ^^^ expected expression
error[E0308]: mismatched types
--> src/main.rs:13:18
|
13 | Ok(f) => io::BufReader::new(f).lines()
| __________________^
14 | | .map(|line| line.unwrap()),
| |______________________________________^ expected trait object `dyn Iterator`, found struct `Map`
|
= note: expected trait object `dyn Iterator<Item = String>`
found struct `Map<std::io::Lines<BufReader<File>>, [closure#src/main.rs:14:18: 14:38]>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to 2 previous errors
I do specifically need a &mut reference to the Iterator in the code I am writing (this is a small reproduction of the problem that I am facing), but I guess that has nothing to do with the issue at hand. I see that there is an impl on Iterator for Map, but the error I get is that this is not the trait object for Iterator<Item = String>. I also tried this:
let input: &mut dyn Iterator<Item = String> = match file {
Ok(f) => &mut io::BufReader::new(f).lines()
.map(|line| line.unwrap()),
Err(_) => &mut Vec::new().into_iter()
};
which of course didn't work, since the temporary value is being dropped in the statement to which I am returning a reference (which is why I did the let binding thing as the compiler suggested).
EDIT Link to playground - https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=99b8105ecc266e03db2d92cc22610962
You can hold the iterators for each branch in a separate variable outside the match (and also use std::iter::empty() instead of Vec::new().into_iter()):
use std::fs::File;
use std::io::{self, BufRead};
use std::iter;
fn run_against_input(inp: impl Iterator<Item = String>) {
for i in inp {
println!("Input line: {}", i);
}
}
fn main() {
let file = File::open("data.txt");
let mut lines;
let mut no_lines;
let input: &mut dyn Iterator<Item = String> = match file {
Ok(f) => {
lines = io::BufReader::new(f).lines().map(Result::unwrap);
&mut lines
},
Err(_) => {
no_lines = iter::empty();
&mut no_lines
},
};
run_against_input(input);
}
You can use a Box instead of a reference, so the value is owned and won't be dropped:
let mut input: Box<dyn Iterator<Item = String>> = match file {
Ok(f) => Box::new(io::BufReader::new(f).lines().map(|line| line.unwrap())),
Err(_) => Box::new(Vec::new().into_iter()),
};
playground
If you prefer you could also change your function to take a Box instead of a reference:
fn run_against_input(inp: Box<dyn Iterator<Item = String>>) {
Why doesn't the param to write1 need to be mut? It passes the value directly to another function that requires mut access.
Any justification for the need for mut in write1 seems it would apply to write2, but the compiler doesn't agree:
use std::io::Write;
fn main() {
let out = &mut std::io::stdout();
write1(out)
}
fn write1(mut out: impl Write) { // `mut` not required. Adding `mut` produces warning: "variable does not need to be mutable"
write2(out)
}
fn write2(mut out: impl Write) { // `mut` required! Otherwise error "cannot borrow `out` as mutable, as it is not declared as mutable"
writeln!(out, "hi").unwrap();
}
playground link
In write1, you're not mutating out, you're giving it (moving it) to write2, just like the main moves it to write1.
write2 is maybe clearer like this:
fn write2(out: impl Write) {
let mut out = out;
writeln!(out, "hi").unwrap();
}
It would be very different with references. In this case you'd have, as you intuited, to propagate the mut:
fn main() {
let mut out = &mut std::io::stdout();
write1(&mut out)
}
fn write1(out: &mut impl Write) {
write2(out)
}
fn write2(out: &mut impl Write) {
writeln!(out, "hi").unwrap();
}
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
fn main() {
let args = env::args().collect::<Vec<String>>();
let file = File::open(&args[1]).expect("file not found");
let reader = BufReader::new(file);
let mut writer = BufWriter::new(std::io::stdout());
for it in reader.bytes() {
writer.write(&[*it]);
}
}
Why does this give an error?
type `std::result::Result<u8, std::io::Error>` cannot be dereferenced
From the documentation,
fn bytes(self) -> Bytes<Self> where
Self: Sized,
Transforms this Read instance to an Iterator over its bytes.
The returned type implements Iterator where the Item is Result<u8,
io::Error>. The yielded item is Ok if a byte was successfully read and
Err otherwise. EOF is mapped to returning None from this iterator.
Only types implementing std::ops::Deref can be dereferenced.
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
fn main() {
let args = env::args().collect::<Vec<String>>();
let file = File::open(&args[1]).expect("file not found");
let reader = BufReader::new(file);
let mut writer = BufWriter::new(std::io::stdout());
for it in reader.bytes() {
writer.write(&[it.unwrap()]);
}
}
The following code is from an exercise in Packt, Mastering Rust 2nd Edition, but no solution is given. The book is a bit old and perhaps there have been changes in the compiler.
The problem is the "word_counter.increment(word);" line where the compiler complains that the Copy trait is not implemented. But I don't see where there is a move.
// word_counter.rs
use std::env;
use std::fs::File;
use std::io::prelude::BufRead;
use std::io::BufReader;
use std::collections::HashMap;
#[derive(Debug)]
struct WordCounter(HashMap<String, u64>); //A New Type Idiom of Tuple Struct
impl WordCounter {
pub fn new() -> WordCounter {
let mut _cmd = WordCounter(HashMap::new());
_cmd
}
pub fn increment(mut self, word: &str) {
let key = word.to_string();
let count = self.0.entry(key).or_insert(0);
*count += 1;
}
pub fn display(self) {
for (key, value) in self.0.iter() {
println!("{}: {}", key, value);
}
}
}
fn main() {
let arguments: Vec<String> = env::args().collect();
let filename = &arguments[1];
println!("Processing file: {}", filename);
let file = File::open(filename).expect("Could not open file");
let reader = BufReader::new(file);
let word_counter = WordCounter::new();
for line in reader.lines() {
let line = line.expect("Could not read line");
let words = line.split(" ");
for word in words {
if word == "" {
continue
} else {
word_counter.increment(word);
}
}
}
word_counter.display();
}
Playground error:
|
31 | let word_counter = WordCounter::new();
| ------------ move occurs because `word_counter` has type `WordCounter`, which does not implement the `Copy` trait
...
40 | word_counter.increment(www);
| ^^^^^^^^^^^^ value moved here, in previous iteration of loop
The signature of increment is
pub fn increment(mut self, word: &str)
The fact that it is mut self and not &mut self means that the function will consume itself (it moves self into the function). Therefore, the first call moves the value, and the second call is illegal.
I'm trying to read some lines from a file, skipping the first few and printing the rest, but I keep getting errors about used value after move:
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read};
use std::path::Path;
fn skip_and_print_file(skip: &usize, path: &Path) {
let mut skip: usize = *skip;
if let Ok(file) = File::open(path) {
let mut buffer = BufReader::new(file);
for (index, line) in buffer.lines().enumerate() {
if index >= skip {
break;
}
}
print_to_stdout(&mut buffer);
}
}
fn print_to_stdout(mut input: &mut Read) {
let mut stdout = io::stdout();
io::copy(&mut input, &mut stdout);
}
fn main() {}
This is the error I'm getting:
error[E0382]: use of moved value: `buffer`
--> src/main.rs:15:30
|
10 | for (index, line) in buffer.lines().enumerate() {
| ------ value moved here
...
15 | print_to_stdout(&mut buffer);
| ^^^^^^ value used here after move
|
= note: move occurs because `buffer` has type `std::io::BufReader<std::fs::File>`, which does not implement the `Copy` trait
In order to avoid the move, use the Read::by_ref() method. That way, you only borrow the BufReader:
for (index, line) in buffer.by_ref().lines().enumerate() { ... }
// ^^^^^^^^^
// you can still use `buffer` here
As Lukas Kalbertodt says, use Read::by_ref.
This prevents lines from consuming the BufReader and instead it consumes a &mut BufReader. The same logic applies to iterators.
Instead of implementing skip yourself, you can use Iterator::take. This has to be driven to completion with a for loop though:
use std::{
fs::File,
io::{self, BufRead, BufReader, Read},
path::Path,
};
fn skip_and_print_file(skip: usize, path: impl AsRef<Path>) {
if let Ok(file) = File::open(path) {
let mut buffer = BufReader::new(file);
for _ in buffer.by_ref().lines().take(skip) {}
// Or: buffer.by_ref().lines().take(skip).for_each(drop);
print_to_stdout(buffer);
}
}
fn print_to_stdout(mut input: impl Read) {
let mut stdout = io::stdout();
io::copy(&mut input, &mut stdout).expect("Unable to copy");
}
fn main() {
skip_and_print_file(2, "/etc/hosts");
}
Note that there's no reason to make the skip variable mutable or even to pass in a reference. You can also take in AsRef<Path> and then callers of skip_and_print_file can just pass in a string literal.