改变循环中的两个相关值
Mutating two dependent values in loop
我尝试以一种足以满足我的目的的方式读取文件。我有一个文件 ID、名称和行索引(有序)的列表,对于每一对 (file_id, file_name, line_index)
我需要打开文件,按索引找到行并打印。
为了提高性能(我知道输入是有序的)我想缓存按行读取的 BufReader
并尽可能让文件保持打开状态。
fn main() {
// positions in file
// structure: (file_id, file_name, line_index_in_file)
let positions = &vec![
(1, String::from("file1"), 1),
(1, String::from("file1"), 2),
(1, String::from("file1"), 20),
(2, String::from("file2"), 15)];
print_lines_from_file(&positions);
}
fn print_lines_from_file(found: &Vec<(i32, String, i32)>) {
let mut last_file_id = -1;
//let mut last_file_name = None;
let mut open_file = None;
let mut open_reader = None;
for &(file_id, ref file_name, pos_in_file) in found {
println!("{} {}", file_id, pos_in_file);
if last_file_id < file_id {
last_file_id = file_id;
//last_file_name = file_ids.get(&file_id);
if let Some(to_close) = open_file {
drop(open_reader.unwrap());
drop(to_close);
}
//let file = File::open(last_file_name.unwrap()).unwrap();
let file = File::open(file_name).unwrap();
open_file = Some(file);
open_reader = Some(BufReader::new(&file));
}
// use reader to find the line in file and process
}
}
我遇到了这个问题:
main.rs:40:48: 40:52 error: `file` does not live long enough
main.rs:40 open_reader = Some(BufReader::new(&file));
main.rs:40:48: 40:52 error: use of moved value: `file` [E0382]
main.rs:40 open_reader = Some(BufReader::new(&file));
很明显(file
的生命周期真的很短),但我不知道如何解决它。
BufReader
依赖于 File
,但是当 file_id
发生变化时,我需要稍后在循环中关闭 File
。
另外,我觉得在循环中以这种方式调用 drop
感觉不太舒服,因为在我看来我试图愚弄编译器。这个方法可以吗?
即使您知道更好的解决方案(例如如何通过 BufReader
关闭文件,我将不胜感激如何解决它的一般见解)。
您可以将 File
按值传递给 BufReader
。这样你就只有一个拥有文件句柄的变量。您可以在 Option
上使用 take
将内部值移出它并留下 None
。这样你可以确保文件句柄在下一个文件被占用之前被释放(所以如果你重新打开同一个文件它不会恐慌)
let mut open_reader = None;
for &(file_id, ref file_name, pos_in_file) in found {
println!("{} {}", file_id, pos_in_file);
if last_file_id < file_id {
last_file_id = file_id;
//last_file_name = file_ids.get(&file_id);
// take the value out of the `open_reader` to make sure that
// the file is closed, so we don't panic if the next statement
// tries to open the same file again.
open_reader.take();
//let file = File::open(last_file_name.unwrap()).unwrap();
let file = File::open(file_name).unwrap();
open_reader = Some(BufReader::new(file));
}
// use reader to find the line in file and process
}
您将文件的所有权授予 BufReader
(这很明显,因为它是按值传递的),而不是借出它 - 现在 BufReader
的工作是关闭文件.掉落时,其拥有的File
会依次掉落;所以你可以完全失去 open_file
。
编译器已成功阻止您破坏 BufReader
脚下的文件。
I'd like to cache the BufReader
that reads by line and let the file stay open if possible.
最简单的方法是提前对数据进行分组:
use std::collections::HashMap;
fn print_lines_from_file(found: &[(i32, String, i32)]) {
let mut index = HashMap::new();
for line in found {
let name = &line.1;
index.entry(name).or_insert_with(Vec::new).push(line);
}
for (file_name, lines) in &index {
let file = File::open(file_name).unwrap();
for &&(file_id, _, line_index) in lines {
// do something with `file`
println!("processing ID {} ({}) line {}", file_id, file_name, line_index);
}
}
}
请注意,这使您不必为 file_id
设置特殊标记值(也可以使用 Option
来完成)。此外,即使您说数据已排序,这也允许您处理 file_id
未排序的情况。您还可以通过在矢量完成后对其进行排序来处理未排序 line_index
es 的情况。
此外:
- 你在
main
中有一个双重引用——你不需要说 &vec![...]
。
- 您应该接受
&[T]
而不是 &Vec<T>
。
恕我直言,一个更漂亮的解决方案是使用 itertools, specifically group_by_lazy
:
extern crate itertools;
use itertools::Itertools;
use std::fs::File;
use std::io::BufReader;
fn main() {
// structure: (file_id, file_name, line_index_in_file)
let positions = [
(1, String::from("file1"), 1),
(1, String::from("file1"), 2),
(1, String::from("file1"), 20),
(2, String::from("file2"), 15)
];
print_lines_from_file(&positions);
}
fn print_lines_from_file(found: &[(i32, String, i32)]) {
for (filename, positions) in &found.iter().group_by_lazy(|pos| &pos.1) {
println!("Opening file {}", filename);
// let file = File::open(file_name).expect("Failed to open the file");
// let file = BufReader::new(file);
for &(id, _, line) in positions {
println!("Processing ID {}, line {}", id, line);
}
}
}
我尝试以一种足以满足我的目的的方式读取文件。我有一个文件 ID、名称和行索引(有序)的列表,对于每一对 (file_id, file_name, line_index)
我需要打开文件,按索引找到行并打印。
为了提高性能(我知道输入是有序的)我想缓存按行读取的 BufReader
并尽可能让文件保持打开状态。
fn main() {
// positions in file
// structure: (file_id, file_name, line_index_in_file)
let positions = &vec![
(1, String::from("file1"), 1),
(1, String::from("file1"), 2),
(1, String::from("file1"), 20),
(2, String::from("file2"), 15)];
print_lines_from_file(&positions);
}
fn print_lines_from_file(found: &Vec<(i32, String, i32)>) {
let mut last_file_id = -1;
//let mut last_file_name = None;
let mut open_file = None;
let mut open_reader = None;
for &(file_id, ref file_name, pos_in_file) in found {
println!("{} {}", file_id, pos_in_file);
if last_file_id < file_id {
last_file_id = file_id;
//last_file_name = file_ids.get(&file_id);
if let Some(to_close) = open_file {
drop(open_reader.unwrap());
drop(to_close);
}
//let file = File::open(last_file_name.unwrap()).unwrap();
let file = File::open(file_name).unwrap();
open_file = Some(file);
open_reader = Some(BufReader::new(&file));
}
// use reader to find the line in file and process
}
}
我遇到了这个问题:
main.rs:40:48: 40:52 error: `file` does not live long enough
main.rs:40 open_reader = Some(BufReader::new(&file));
main.rs:40:48: 40:52 error: use of moved value: `file` [E0382]
main.rs:40 open_reader = Some(BufReader::new(&file));
很明显(file
的生命周期真的很短),但我不知道如何解决它。
BufReader
依赖于 File
,但是当 file_id
发生变化时,我需要稍后在循环中关闭 File
。
另外,我觉得在循环中以这种方式调用 drop
感觉不太舒服,因为在我看来我试图愚弄编译器。这个方法可以吗?
即使您知道更好的解决方案(例如如何通过 BufReader
关闭文件,我将不胜感激如何解决它的一般见解)。
您可以将 File
按值传递给 BufReader
。这样你就只有一个拥有文件句柄的变量。您可以在 Option
上使用 take
将内部值移出它并留下 None
。这样你可以确保文件句柄在下一个文件被占用之前被释放(所以如果你重新打开同一个文件它不会恐慌)
let mut open_reader = None;
for &(file_id, ref file_name, pos_in_file) in found {
println!("{} {}", file_id, pos_in_file);
if last_file_id < file_id {
last_file_id = file_id;
//last_file_name = file_ids.get(&file_id);
// take the value out of the `open_reader` to make sure that
// the file is closed, so we don't panic if the next statement
// tries to open the same file again.
open_reader.take();
//let file = File::open(last_file_name.unwrap()).unwrap();
let file = File::open(file_name).unwrap();
open_reader = Some(BufReader::new(file));
}
// use reader to find the line in file and process
}
您将文件的所有权授予 BufReader
(这很明显,因为它是按值传递的),而不是借出它 - 现在 BufReader
的工作是关闭文件.掉落时,其拥有的File
会依次掉落;所以你可以完全失去 open_file
。
编译器已成功阻止您破坏 BufReader
脚下的文件。
I'd like to cache the
BufReader
that reads by line and let the file stay open if possible.
最简单的方法是提前对数据进行分组:
use std::collections::HashMap;
fn print_lines_from_file(found: &[(i32, String, i32)]) {
let mut index = HashMap::new();
for line in found {
let name = &line.1;
index.entry(name).or_insert_with(Vec::new).push(line);
}
for (file_name, lines) in &index {
let file = File::open(file_name).unwrap();
for &&(file_id, _, line_index) in lines {
// do something with `file`
println!("processing ID {} ({}) line {}", file_id, file_name, line_index);
}
}
}
请注意,这使您不必为 file_id
设置特殊标记值(也可以使用 Option
来完成)。此外,即使您说数据已排序,这也允许您处理 file_id
未排序的情况。您还可以通过在矢量完成后对其进行排序来处理未排序 line_index
es 的情况。
此外:
- 你在
main
中有一个双重引用——你不需要说&vec![...]
。 - 您应该接受
&[T]
而不是&Vec<T>
。
恕我直言,一个更漂亮的解决方案是使用 itertools, specifically group_by_lazy
:
extern crate itertools;
use itertools::Itertools;
use std::fs::File;
use std::io::BufReader;
fn main() {
// structure: (file_id, file_name, line_index_in_file)
let positions = [
(1, String::from("file1"), 1),
(1, String::from("file1"), 2),
(1, String::from("file1"), 20),
(2, String::from("file2"), 15)
];
print_lines_from_file(&positions);
}
fn print_lines_from_file(found: &[(i32, String, i32)]) {
for (filename, positions) in &found.iter().group_by_lazy(|pos| &pos.1) {
println!("Opening file {}", filename);
// let file = File::open(file_name).expect("Failed to open the file");
// let file = BufReader::new(file);
for &(id, _, line) in positions {
println!("Processing ID {}, line {}", id, line);
}
}
}