I'm a little bit stuck with a in a first glance so simple problem. I want to write a simple Rust program that depending on the file extension can read normal CSV files as well as zipped CSV files and outputs the content of these files. Well, of course, the real problem is a little bit differnt but I'm stuck before I can even start to solve the actual problem. :-D
What I tried was something like this:
use std::io::Error;
use std::fs::File;
use std::io;
use csv::Reader;
fn read_csv(filename: &str) -> Result<(), Error> {
let f = std::fs::File::open(filename)?;
// Want to return a reader object which either reads from ZipFile or from "normal" File
let mut rdr: Reader<Box<dyn io::Read>> =
if filename.ends_with("zip") { // zip case
let mut ar = zip::read::ZipArchive::new(f)?;
let zf = ar.by_index(0)?;
csv::ReaderBuilder::new().delimiter(b',').from_reader(Box::new(zf))
} else { // csv case
csv::ReaderBuilder::new().delimiter(b',').from_reader(Box::new(f))
};
// From here on, I don't want to care whether I'm reading from a zipped CSV or normal CSV
for rec in rdr.records() {
rec?.iter().for_each(|v| println!("{},", v));
}
Ok(())
}
fn main() -> Result<(), Error> {
let filename_csv = "tmp.csv";
let filename_zip = "tmp.csv.zip"; // same as tmp.csv but zipped
read_csv(filename_csv);
read_csv(filename_zip);
Ok(())
}
I.e., depending on the filename, I'm trying to either create a CSV reader from a ZipFile object or from a normal File object. However, when I try to compile this program, the compiler correctly tells me:
error[E0597]: `ar` does not live long enough
--> src/main.rs:13:22
|
10 | let mut rdr: Reader<Box<dyn io::Read>> =
| ------- borrow later stored here
...
13 | let zf = ar.by_index(0)?;
| ^^ borrowed value does not live long enough
14 | csv::ReaderBuilder::new().delimiter(b',').from_reader(Box::new(zf))
15 | } else { // csv case
| - `ar` dropped here while still borrowed
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0597`.
error: could not compile `zipped-csv-read`
This makes sense to me, since ar will "die" as soon as the if-block is finished and then it cannot be used in the reader object. But what I do not understand how to restructure the code such that it becomes safe but without too much boilerplate code (like moving the for-loop inside of both if-else-branches). I'm coming from the Python, Java, ... world where the above code would "just work". I'm glad that Rust shows me that Python and Java programs would then probably actually suck from the safety point of view... but I need a hint from someone with Rust experience how to make the code better.
Best and thanks, Sergej
P.S.: Dependencies in Cargo.toml (just for completeness):
[dependencies]
zip = "0.5"
csv = "1.1"