Введение в работу с файловой системой в Rust
Rust — современный системный язык программирования, который уделяет огромное внимание безопасности, производительности и контролю над памятью. Работа с файловой системой — одна из фундаментальных задач, с которой сталкивается каждый разработчик. В этой статье мы подробно разберем, как читать и записывать файлы, создавать директории, обрабатывать ошибки и использовать стандартную библиотеку std::fs для эффективного взаимодействия с файловой системой.
Мы начнем с самых основ и постепенно перейдем к более сложным сценариям. Все примеры кода написаны на Rust и готовы к запуску. Если вы только начинаете изучать Rust, эта статья станет отличной отправной точкой для освоения ввода-вывода (I/O) в этом языке.
Основные модули и типы для работы с файловой системой
В Rust вся функциональность для работы с файлами и директориями сосредоточена в модуле std::fs. Кроме того, важную роль играют типы Path и PathBuf из модуля std::path, которые представляют пути к файлам и директориям.
Типы Path и PathBuf
Path — это слайс (срез) для работы с путями без владения данными. PathBuf — это владеющая версия, которую можно изменять. Они предоставляют удобные методы для манипуляции путями.
use std::path::Path;
use std::path::PathBuf;
fn main() {
// Создание PathBuf
let mut path = PathBuf::from("/tmp");
path.push("example.txt");
println!("Путь: {}", path.display());
// Преобразование в Path
let path_ref: &Path = path.as_path();
println!("Родительская директория: {:?}", path_ref.parent());
}
Модуль std::fs
Модуль std::fs содержит основные функции для работы с файловой системой. Вот наиболее часто используемые:
read_to_string— чтение файла в строкуwrite— запись данных в файлcreate_dirиcreate_dir_all— создание директорийremove_fileиremove_dir— удаление файлов и директорийmetadata— получение информации о файлеcopyиrename— копирование и переименование
Чтение и запись файлов в Rust
Самый простой способ прочитать или записать файл — использовать функции-обертки, которые выполняют всю работу за один вызов. Они подходят для небольших файлов, которые целиком помещаются в память.
Чтение файла целиком
use std::fs;
fn main() -> Result<(), std::io::Error> {
let content = fs::read_to_string("hello.txt")?;
println!("Содержимое файла:\n{}", content);
Ok(())
}
Функция read_to_string возвращает Result<String>. Если файл не существует или нет прав на чтение, будет возвращена ошибка. Обратите внимание на оператор ? — он автоматически пробрасывает ошибку наверх.
Запись в файл
use std::fs;
fn main() -> Result<(), std::io::Error> {
let data = "Привет, Rust!";
fs::write("output.txt", data)?;
println!("Файл записан успешно");
Ok(())
}
Функция write перезаписывает файл, если он существует, или создает новый. Для добавления данных в конец файла используйте OpenOptions.
Работа с бинарными файлами
Для чтения и записи бинарных данных используйте read и write с вектором байтов:
use std::fs;
fn main() -> Result<(), std::io::Error> {
// Запись бинарных данных
let bytes: Vec<u8> = vec![0, 1, 2, 3, 255];
fs::write("binary.dat", &bytes)?;
// Чтение как байты
let read_bytes = fs::read("binary.dat")?;
println!("Прочитано байт: {}", read_bytes.len());
Ok(())
}
Работа с директориями и навигация по файловой системе
Rust предоставляет удобные инструменты для создания, удаления и обхода директорий.
Создание и удаление директорий
use std::fs;
fn main() -> Result<(), std::io::Error> {
// Создание одной директории
fs::create_dir("my_folder")?;
// Создание вложенных директорий (как mkdir -p)
fs::create_dir_all("a/b/c/d")?;
// Удаление пустой директории
fs::remove_dir("my_folder")?;
// Удаление директории со всем содержимым (осторожно!)
fs::remove_dir_all("a")?;
Ok(())
}
Чтение содержимого директории
use std::fs;
fn main() -> Result<(), std::io::Error> {
let entries = fs::read_dir(".")?;
for entry in entries {
let entry = entry?;
let path = entry.path();
let file_type = entry.file_type()?;
if file_type.is_dir() {
println!("[DIR] {}", path.display());
} else if file_type.is_file() {
println!("[FILE] {}", path.display());
}
}
Ok(())
}
Получение метаданных файла
use std::fs;
fn main() -> Result<(), std::io::Error> {
let metadata = fs::metadata("Cargo.toml")?;
println!("Размер: {} байт", metadata.len());
println!("Только для чтения: {}", metadata.permissions().readonly());
println!("Время изменения: {:?}", metadata.modified());
Ok(())
}
Продвинутые техники: буферизация, OpenOptions и обработка ошибок
Для работы с большими файлами или когда нужен тонкий контроль над процессом, используйте буферизированное чтение/запись и структуру OpenOptions.
Использование OpenOptions
OpenOptions позволяет точно настроить режим открытия файла: чтение, запись, добавление, создание и т.д.
use std::fs