Rust and Files: From Beginner to Master of File System Operations

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Working with Files in Rust is Not a Pain, but a Pleasure?

\n\n

If you're used to Python, where open() solves almost everything, Rust might seem like a strict teacher. But behind this strictness lies incredible power. Rust won't let you accidentally leave a file open, read bytes as a string, or forget to check if a directory exists. In this article, we'll explore working with files and the file system in Rust — from simple operations to advanced techniques. You'll learn how to read, write, create, and delete files, as well as how to efficiently manage paths.

\n\n

Ready? Let's go!

\n\n

Basics: Open, Read, Close

\n\n

Rust doesn't have a built-in open() function like Python. Instead, we use the powerful std::fs module. Let's start with the simplest — reading an entire file.

\n\n

Reading an Entire File into a String

\n\n

The simplest way to read a file is to use std::fs::read_to_string. This is analogous to file.read() in Python, but with safety guarantees.

\n\n
use std::fs;\n\nfn main() {\n    let content = fs::read_to_string("hello.txt")\n        .expect("Failed to read file");\n    println!("File contents:\\n{}", content);\n}
\n\n

Notice the .expect(). Rust doesn't have exceptions — it has Result. If the file doesn't exist, the program will "panic" with a clear message. For production code, it's better to use match or unwrap_or_else, but for learning, expect is ideal.

\n\n

Writing to a File

\n\n

Writing is also intuitive:

\n\n
use std::fs;\n\nfn main() {\n    let data = "Hello, Rust!\\nThis is a test write.";\n    fs::write("output.txt", data)\n        .expect("Failed to write file");\n    println!("File successfully written!");\n}
\n\n

The fs::write method creates the file if it doesn't exist and overwrites the contents if the file exists. To append data to the end of a file, use OpenOptions.

\n\n

Working with Paths: Path and PathBuf

\n\n

Rust has two main types for working with paths: Path (borrowed, immutable) and PathBuf (owned, mutable). Understanding the difference between them is key to efficient file system operations.

\n\n

Creating and Manipulating Paths

\n\n
use std::path::{Path, PathBuf};\n\nfn main() {\n    // Create a path\n    let path = Path::new("data/subdir/file.txt");\n    \n    // Get path components\n    println!("Parent directory: {:?}", path.parent());\n    println!("File name: {:?}", path.file_name());\n    println!("Extension: {:?}", path.extension());\n    \n    // Build a new path\n    let mut new_path = PathBuf::from("backup");\n    new_path.push("data");\n    new_path.push("file.txt");\n    println!("New path: {}", new_path.display());\n}
\n\n

The .display() method is mandatory if you want to print the path to the terminal. Rust doesn't allow simply inserting a path into a string — this is protection against encoding issues.

\n\n

Reading and Writing: Advanced Level

\n\n

When you need more control, use File and BufReader/BufWriter.

\n\n

Reading a File Line by Line

\n\n

For large files, reading everything into memory is inefficient. Use buffered reading:

\n\n
use std::fs::File;\nuse std::io::{BufRead, BufReader};\n\nfn main() {\n    let file = File::open("large_file.txt")\n        .expect("File not found");\n    let reader = BufReader::new(file);\n    \n    for (index, line) in reader.lines().enumerate() {\n        let line = line.expect("Error reading line");\n        println!("Line {}: {}", index + 1, line);\n    }\n}
\n\n

This approach allows you to process files of any size without loading them entirely into RAM.

\n\n

Appending Data to the End of a File

\n\n

Blogs

Book Recommendations