📚 Rust File I/O: Your Computer’s Filing Cabinet
The Big Picture: A Magical Filing Cabinet 🗄️
Imagine your computer has a giant magical filing cabinet. Inside are folders and papers (files) with all kinds of information—photos, stories, game saves, and more!
File I/O (Input/Output) is how your Rust program:
- Opens the cabinet drawer
- Reads papers inside (input)
- Writes new papers or edits old ones (output)
- Closes the drawer when done
It’s like being a librarian for your computer!
🎯 What You’ll Learn
graph TD A["std::io Module"] --> B["File I/O Basics"] B --> C["Reading Files"] B --> D["Writing Files"] C --> E["BufReader"] D --> F["BufWriter"]
1️⃣ The std::io Module: Your Toolkit
Think of std::io as your librarian’s toolkit. It has everything you need to work with files!
What’s Inside the Toolkit?
| Tool | What It Does |
|---|---|
Read |
Lets you read stuff |
Write |
Lets you write stuff |
BufRead |
Read smarter, faster |
Result |
Tells you if it worked |
Getting Your Toolkit
use std::io;
use std::io::Read;
use std::io::Write;
Simple Example:
use std::io;
fn main() {
// io gives us tools
// for reading and writing!
println!("Toolkit ready!");
}
💡 Remember:
std::iois always available. No extra downloads needed!
2️⃣ File I/O: Opening the Cabinet
Before reading or writing, you need to open a file. Think of it like pulling out a specific folder from the cabinet.
Meet std::fs::File
use std::fs::File;
File is your way to grab a specific paper from the cabinet!
Opening a File
use std::fs::File;
fn main() {
// Try to open a file
let file = File::open("story.txt");
match file {
Ok(f) => println!("Got it!"),
Err(e) => println!("Oops: {}", e),
}
}
Why match?
- The file might not exist! 🤷
- The match is like asking: “Did we find it or not?”
The Golden Rule
graph TD A["Want to Read?"] -->|Yes| B["File::open"] A -->|No, Write| C["File::create"] B --> D["Opens existing file"] C --> E["Makes new file"]
3️⃣ Reading Files: Getting Information Out
Reading is like looking at a paper in your filing cabinet.
Method 1: Read Everything at Once
use std::fs;
fn main() {
// Read entire file to string
let content = fs::read_to_string(
"hello.txt"
);
match content {
Ok(text) => println!("{}", text),
Err(e) => println!("Error: {}", e),
}
}
When to use: Small files, like notes or configs.
Method 2: Read into Bytes
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = File::open("data.txt")
.expect("Can't open file");
let mut buffer = String::new();
file.read_to_string(&mut buffer)
.expect("Can't read");
println!("{}", buffer);
}
Why mut? We’re filling the buffer, so it changes!
The Reading Flow
graph TD A["Open File"] --> B["Create Buffer"] B --> C["Read into Buffer"] C --> D["Use the Data!"] D --> E["File Closes Automatically"]
🎉 Fun Fact: Rust automatically closes files when you’re done. No mess left behind!
4️⃣ Writing Files: Adding New Papers
Writing is like putting a new paper into your cabinet.
Creating a New File
use std::fs::File;
use std::io::Write;
fn main() {
let mut file = File::create("note.txt")
.expect("Can't create file");
file.write_all(b"Hello, Rust!")
.expect("Can't write");
println!("Done writing!");
}
What’s that b? It turns text into bytes. Files love bytes!
Appending to a File
Want to add to a file without erasing it?
use std::fs::OpenOptions;
use std::io::Write;
fn main() {
let mut file = OpenOptions::new()
.append(true)
.open("diary.txt")
.expect("Can't open");
file.write_all(b"\nNew entry!")
.expect("Can't write");
}
OpenOptions is like choosing how to open the cabinet:
append(true)= add to the endwrite(true)= allow writingcreate(true)= make if missing
Writing Cheat Sheet
| Goal | Method |
|---|---|
| New file, fresh start | File::create() |
| Add to existing | OpenOptions.append(true) |
| Write if exists | OpenOptions.write(true) |
5️⃣ BufReader: The Speed Reader 🚀
Imagine reading a book one letter at a time. Slow, right?
BufReader reads in chunks (buffers). Much faster!
Without BufReader (Slow)
// Reads tiny bits, asks disk a lot
// Like asking librarian for
// each letter individually!
With BufReader (Fast!)
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let file = File::open("story.txt")
.expect("Can't open");
let reader = BufReader::new(file);
// Read line by line, super fast!
for line in reader.lines() {
match line {
Ok(text) => println!("{}", text),
Err(_) => break,
}
}
}
Why BufReader Rocks
graph LR A["File on Disk"] -->|Small chunks| B["Slow Reading"] A -->|Big buffer| C["BufReader"] C -->|Gives lines| D["Your Program"] D -->|Fast!| E["Happy Coder"]
Reading Lines Made Easy
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let file = File::open("list.txt")
.expect("Oops!");
let reader = BufReader::new(file);
for (num, line) in
reader.lines().enumerate()
{
if let Ok(text) = line {
println!("{}: {}", num+1, text);
}
}
}
This prints each line with its number!
6️⃣ BufWriter: The Smart Writer 🖊️
Just like BufReader helps reading, BufWriter makes writing faster!
The Problem
Writing one tiny thing at a time is slow. Your program waits for the disk each time.
The Solution
use std::fs::File;
use std::io::{BufWriter, Write};
fn main() {
let file = File::create("output.txt")
.expect("Can't create");
let mut writer = BufWriter::new(file);
// Write lots of things fast!
writer.write_all(b"Line 1\n")
.expect("Write failed");
writer.write_all(b"Line 2\n")
.expect("Write failed");
writer.write_all(b"Line 3\n")
.expect("Write failed");
// Flush to make sure it's saved
writer.flush()
.expect("Flush failed");
}
How BufWriter Works
graph TD A["Your writes"] --> B["Buffer fills up"] B --> C{Buffer full?} C -->|Yes| D["Write to disk"] C -->|No| E["Keep buffering"] D --> E E --> F["Flush at end"]
Why Flush Matters
flush() pushes everything from the buffer to the file. Think of it like:
- Without flush: Writing in invisible ink
- With flush: Ink becomes permanent!
⚠️ Important: BufWriter flushes automatically when dropped, but calling
flush()is good practice!
🎨 Putting It All Together
Here’s a complete example that reads, modifies, and writes:
use std::fs::File;
use std::io::{
BufRead, BufReader,
BufWriter, Write
};
fn main() {
// Read input file
let input = File::open("input.txt")
.expect("No input file!");
let reader = BufReader::new(input);
// Create output file
let output = File::create("output.txt")
.expect("Can't create output!");
let mut writer = BufWriter::new(output);
// Copy lines, add numbers
for (i, line) in
reader.lines().enumerate()
{
if let Ok(text) = line {
let numbered = format!(
"{}. {}\n", i+1, text
);
writer.write_all(
numbered.as_bytes()
).expect("Write error!");
}
}
writer.flush().expect("Flush error!");
println!("Done! Check output.txt");
}
🧠 Quick Reference
| Task | Code |
|---|---|
| Read whole file | fs::read_to_string("f.txt") |
| Open for reading | File::open("f.txt") |
| Create new file | File::create("f.txt") |
| Fast reading | BufReader::new(file) |
| Fast writing | BufWriter::new(file) |
| Write bytes | file.write_all(b"text") |
| Read lines | reader.lines() |
| Save buffer | writer.flush() |
🌟 Key Takeaways
- std::io is your toolkit for all input/output
- File::open reads, File::create writes
- BufReader makes reading fast (reads in chunks)
- BufWriter makes writing fast (writes in chunks)
- Always handle errors with
matchorexpect - Files close automatically in Rust!
🚀 You Did It!
You now know how to:
- ✅ Use the std::io module
- ✅ Open, read, and write files
- ✅ Speed up with BufReader and BufWriter
Your Rust programs can now work with files like a pro librarian! 📚✨
Next Step: Try creating a program that reads your favorite quotes from a file and displays them randomly!
