File Input/Output in Rust
About This Module
This module covers file input/output operations in Rust, focusing on reading from and writing to files safely and efficiently. Students will learn how to handle files, work with the std::fs and std::io modules, and implement error handling for file operations. The module includes practical examples of reading and writing different data formats, with a focus on processing large datasets commonly used in data science applications.
Prework
Before this lecture, please read:
- The Rust Book Chapter 12.1: "Accepting Command Line Arguments" - https://doc.rust-lang.org/book/ch12-01-accepting-command-line-arguments.html
- The Rust Book Chapter 9: "Error Handling" - https://doc.rust-lang.org/book/ch09-00-error-handling.html
- The Rust Book Chapter 12.3: "Refining the Library with Tests" - https://doc.rust-lang.org/book/ch12-03-improving-error-handling-and-modularity.html (focus on file I/O sections)
Pre-lecture Reflections
- How does Rust's ownership model affect file handling compared to other languages?
- What are the advantages of Rust's
Resulttype for file I/O error handling? - When would you use buffered vs. unbuffered file reading in data processing applications?
Lecture
Learning Objectives
By the end of this lecture, you should be able to:
- Open, read from, and write to files in Rust using
std::fsandstd::io - Handle file I/O errors properly using
Resulttypes andexpect() - Use buffered readers for efficient file processing
- Parse and generate file formats commonly used in data science
- Implement file-based data processing workflows
File I/O
See also file_io.
:dep rand="0.8.5"
use std::fs::File; // object providing access to an open file on filesystem
use std::io::prelude::*; // imports common I/O traits
use rand::Rng;
fn generate_file(path: &str, n: usize) {
// Generate a random file of edges for vertices 0.n
// Create a file at `path` and return Result<File> or error
// .expect() unwraps the Result<File> or prints error and panics
let mut file = File::create(path).expect("Unable to create file");
for i in 0..n {
// How many neighbors will this node have
let rng = rand::thread_rng().gen_range(0..20) as usize;
for _j in 0..rng {
// Randomly select a neighbor (even with duplicates but not to ourselves)
let neighbor = rand::thread_rng().gen_range(0..n) as usize;
if neighbor != i {
let s: String = format!("{} {}\n", i, neighbor);
file.write_all(s.as_bytes()).expect("Unable to write file");
}
}
}
}
fn read_file(path: &str) -> Vec<(u32, u32)> {
let mut result: Vec<(u32, u32)> = Vec::new();
let file = File::open(path).expect("Could not open file");
let buf_reader = std::io::BufReader::new(file).lines();
for line in buf_reader {
let line_str = line.expect("Error reading");
let v: Vec<&str> = line_str.trim().split(' ').collect();
let x = v[0].parse::<u32>().unwrap();
let y = v[1].parse::<u32>().unwrap();
result.push((x, y));
}
return result;
}
println!("Generating file");
generate_file("list_of_edges.txt", 10);
let edges = read_file("list_of_edges.txt");
println!("{:?}", edges);
Generating file
[(0, 5), (0, 9), (0, 1), (0, 8), (0, 2), (0, 2), (0, 1), (0, 6), (0, 8), (0, 9), (1, 6), (1, 6), (1, 4), (2, 7), (2, 7), (2, 1), (2, 1), (2, 8), (2, 6), (3, 0), (3, 1), (3, 2), (3, 8), (3, 2), (3, 8), (3, 0), (3, 4), (3, 8), (3, 7), (3, 8), (3, 0), (3, 8), (3, 2), (3, 6), (3, 8), (3, 4), (4, 7), (4, 2), (4, 0), (4, 5), (4, 0), (4, 9), (4, 2), (4, 2), (4, 9), (4, 7), (4, 3), (4, 9), (4, 9), (5, 8), (5, 0), (5, 3), (5, 4), (5, 2), (5, 4), (5, 9), (6, 3), (6, 7), (6, 9), (6, 3), (6, 7), (6, 7), (6, 4), (6, 0), (6, 3), (6, 8), (6, 0), (6, 9), (6, 5), (7, 4), (7, 9), (7, 3), (7, 6), (7, 3), (7, 9), (7, 1), (7, 8), (7, 1), (7, 2), (7, 2), (8, 2), (8, 0), (8, 1), (8, 3), (8, 7), (8, 5), (8, 4), (9, 2), (9, 3), (9, 4), (9, 5), (9, 4), (9, 2), (9, 5), (9, 2)]