Practical examples of examples of file I/O in Rust
Let’s start with the most common example of file I/O in Rust: reading an entire text file into memory as a String. This is the “hello world” of file handling and one of the best examples of how ergonomic Rust can be when the standard library does the heavy lifting.
use std::fs;
use std::path::Path;
use std::error::Error;
fn read_config_file(path: &Path) -> Result<String, Box<dyn Error>> {
let contents = fs::read_to_string(path)?;
Ok(contents)
}
fn main() -> Result<(), Box<dyn Error>> {
let path = Path::new("config.toml");
match read_config_file(path) {
Ok(text) => {
println!("Config file contents:\n{}", text);
}
Err(e) => {
eprintln!("Failed to read config: {e}");
}
}
Ok(())
}
This example of file I/O in Rust shows a few idioms:
fs::read_to_stringis perfect for moderately sized text files.- Errors bubble up with
?, which is now the standard Rust style. Pathis preferred over raw strings for filesystem paths.
If you browse the official Rust docs at doc.rust-lang.org, you’ll see similar examples of examples of file I/O in Rust, but here we’re shaping it into a reusable function instead of a one-off snippet.
Writing text files: more examples include append vs overwrite
Another core example of file I/O in Rust is writing text data. The behavior you pick—overwriting vs appending—matters a lot when you’re dealing with logs or generated reports.
Overwriting a file with fs::write
use std::fs;
use std::error::Error;
fn write_report(path: &str, report: &str) -> Result<(), Box<dyn Error>> {
fs::write(path, report)?; // creates or fully overwrites
Ok(())
}
fn main() -> Result<(), Box<dyn Error>> {
let report = "daily_users = 1523\nerrors = 7\n";
write_report("report.txt", report)?;
Ok(())
}
This is one of the best examples when you want a clean file every run, like a daily summary.
Appending to a file with OpenOptions
use std::fs::OpenOptions;
use std::io::Write;
use std::error::Error;
fn append_log_line(path: &str, line: &str) -> Result<(), Box<dyn Error>> {
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(path)?;
writeln!(file, "{line}")?;
Ok(())
}
fn main() -> Result<(), Box<dyn Error>> {
append_log_line("app.log", "2025-01-01T00:00:00Z START")?;
append_log_line("app.log", "2025-01-01T00:00:01Z READY")?;
Ok(())
}
Here, the examples of file I/O in Rust highlight how OpenOptions controls the file mode. This pattern shows up constantly in CLI tools and background services.
For more about file permissions and modes, the Rust book’s standard library section at rust-lang.org offers additional real examples.
Buffered reading: examples of file I/O in Rust for large files
When files get big—log archives, CSV exports, scientific datasets—you don’t want to load everything into memory. In those scenarios, buffered reading is one of the best examples of performance-conscious file I/O.
use std::fs::File;
use std::io::{self, BufRead, BufReader};
fn count_error_lines(path: &str) -> io::Result<usize> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut count = 0;
for line_result in reader.lines() {
let line = line_result?;
if line.contains("ERROR") {
count += 1;
}
}
Ok(count)
}
fn main() -> io::Result<()> {
let errors = count_error_lines("server.log")?;
println!("Found {errors} error lines");
Ok(())
}
This example of file I/O in Rust demonstrates:
BufReaderwrapsFileto reduce system calls.lines()gives you an iterator ofResult<String, io::Error>.- You can stream through gigabyte-scale logs without blowing up RAM.
In data-heavy fields (think large-scale health datasets from sources like the National Center for Health Statistics), patterns like this are the difference between a tool that finishes in seconds and one that thrashes your machine.
Binary data: real examples of reading and writing bytes
Not everything is UTF‑8 text. Images, protocol buffers, and some scientific formats are binary. Here are examples of examples of file I/O in Rust that deal directly with Vec<u8>.
Reading a binary file into memory
use std::fs;
use std::error::Error;
fn read_image_bytes(path: &str) -> Result<Vec<u8>, Box<dyn Error>> {
let bytes = fs::read(path)?; // reads raw bytes
Ok(bytes)
}
fn main() -> Result<(), Box<dyn Error>> {
let img = read_image_bytes("photo.png")?;
println!("Image size: {} bytes", img.len());
Ok(())
}
Writing binary data to disk
use std::fs::File;
use std::io::{self, Write};
fn write_waveform(path: &str, data: &[u8]) -> io::Result<()> {
let mut file = File::create(path)?;
file.write_all(data)?;
Ok(())
}
These real examples are the building blocks behind file formats for audio, images, or even medical imaging data you might later analyze with tools referenced by organizations like the National Institutes of Health.
JSON config: examples include serde for structured file I/O
Modern Rust projects almost always need structured configuration or data exchange. JSON is the go-to format, and serde is the standard crate for serialization.
Here’s an example of file I/O in Rust that reads and writes JSON config files:
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::{self, BufReader, BufWriter};
#[derive(Debug, Serialize, Deserialize)]
struct AppConfig {
host: String,
port: u16,
log_level: String,
}
fn load_config(path: &str) -> io::Result<AppConfig> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let config = serde_json::from_reader(reader)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
Ok(config)
}
fn save_config(path: &str, config: &AppConfig) -> io::Result<()> {
let file = File::create(path)?;
let writer = BufWriter::new(file);
serde_json::to_writer_pretty(writer, config)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(())
}
You can see how these examples of examples of file I/O in Rust combine:
- Buffered I/O (
BufReader,BufWriter). - Error translation from
serde_jsonintoio::Errorso callers have a single error type. - A typed config struct instead of unstructured
Stringparsing.
For developers coming from Python or JavaScript, this pattern feels familiar but with Rust’s type safety on top.
Safer patterns: temporary files and atomic writes
Real examples of file I/O in Rust in production systems often need to avoid corrupting files during crashes or partial writes. A common pattern is to write to a temporary file, then atomically rename it.
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::Path;
fn atomic_write(path: &Path, contents: &str) -> io::Result<()> {
let tmp_path = path.with_extension("tmp");
{
let mut tmp_file = File::create(&tmp_path)?;
tmp_file.write_all(contents.as_bytes())?;
tmp_file.sync_all()?; // flush data to disk
}
fs::rename(tmp_path, path)?; // often atomic on POSIX
Ok(())
}
This is one of the best examples of production-grade file I/O because it reduces the risk of ending up with half-written config files. If you’re writing anything that touches health records, financial data, or other sensitive information (areas frequently discussed by sources like Harvard University), patterns like this are worth adopting early.
Async examples of file I/O in Rust with Tokio (2024–2025 style)
Synchronous file I/O is fine for many tools, but modern Rust backends increasingly rely on async runtimes like Tokio. In 2024–2025, you’ll see more real examples using tokio::fs for non-blocking file operations.
use tokio::fs;
use tokio::io::{self, AsyncBufReadExt, BufReader};
async fn async_read_lines(path: &str) -> io::Result<()> {
let file = fs::File::open(path).await?;
let reader = BufReader::new(file);
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await? {
println!("{line}");
}
Ok(())
}
#[tokio::main]
async fn main() -> io::Result<()> {
async_read_lines("async.log").await
}
In this example of file I/O in Rust, the pattern feels similar to the earlier buffered example, but every operation returns a future. This is particularly useful when your service is handling HTTP requests, talking to databases, and reading files at the same time.
Error handling patterns: examples include thiserror and anyhow
As your codebase grows, you’ll want cleaner error types around file I/O. While the standard library’s io::Error works, many 2024–2025 Rust projects use crates like thiserror or anyhow.
Here’s a small example of examples of file I/O in Rust with a custom error type:
use std::{fs, io, path::PathBuf};
use thiserror::Error;
#[derive(Debug, Error)]
enum ConfigError {
#[error("I/O error while accessing {path:?}: {source}")]
Io {
#[source]
source: io::Error,
path: PathBuf,
},
#[error("Invalid UTF-8 in config file {0:?}")]
InvalidUtf8(PathBuf),
}
fn read_utf8_config(path: PathBuf) -> Result<String, ConfigError> {
let bytes = fs::read(&path).map_err(|source| ConfigError::Io {
source,
path: path.clone(),
})?;
String::from_utf8(bytes).map_err(|_| ConfigError::InvalidUtf8(path))
}
This is not just syntactic sugar. These real examples lead to better error messages and log output, which matters when you’re debugging file permission issues on a production server.
FAQ: short answers with concrete examples
What are some simple examples of file I/O in Rust for beginners?
Some of the simplest examples of file I/O in Rust are:
- Using
fs::read_to_string("file.txt")to read a whole text file. - Using
fs::write("out.txt", "hello")to create or overwrite a file. - Using
OpenOptions::new().append(true)to add lines to a log file.
These examples include the core operations you’ll use in most small tools.
Can you show an example of reading a file line by line?
Yes. A classic example of file I/O in Rust for line-by-line processing is:
use std::fs::File;
use std::io::{self, BufRead, BufReader};
fn read_lines(path: &str) -> io::Result<()> {
let file = File::open(path)?;
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line?);
}
Ok(())
}
What are the best examples of async file I/O in Rust today?
The best examples in current Rust projects usually rely on Tokio’s tokio::fs module. Opening files with tokio::fs::File::open, wrapping them in BufReader, and using lines() or read_exact gives you non-blocking behavior that plays nicely with async HTTP servers and databases.
Where can I learn more about Rust file I/O patterns?
The official Rust documentation at doc.rust-lang.org includes detailed reference material and examples. For broader context on secure coding and safe handling of sensitive data that might live in files, organizations like the NIH and CDC publish guidelines that, while not Rust-specific, are very relevant when your file I/O touches real-world health or research data.
If you keep these examples of examples of file I/O in Rust in your back pocket—simple reads and writes, buffered streaming, binary data, JSON with serde, atomic writes, and async patterns—you’ll be in good shape for most 2025-era Rust applications.
Related Topics
Examples of Basic Syntax in Rust: 3 Practical Examples for Beginners
Best examples of defining functions in Rust: practical examples for 2025
Examples of Using Enums in Rust (With Practical Code Snippets)
Real-world examples of examples of testing in Rust
Practical examples of examples of using closures in Rust
Practical examples of examples of file I/O in Rust
Explore More Rust Code Snippets
Discover more examples and insights in this category.
View All Rust Code Snippets