Работа с файловой системой в Rust: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в работу с файловой системой в Rust

Rust — это современный системный язык программирования, который предлагает мощные и безопасные средства для взаимодействия с файловой системой. В отличие от многих других языков, Rust гарантирует безопасность работы с памятью и потоками, что особенно важно при операциях ввода-вывода. В этой статье мы подробно разберём, как создавать, читать, записывать файлы и директории, управлять путями и обрабатывать ошибки с помощью стандартной библиотеки std::fs и std::path.

Материал будет полезен как новичкам, которые только знакомятся с Rust, так и опытным разработчикам, желающим освежить знания. Все примеры кода готовы к запуску и сопровождаются комментариями.

Основные модули и импорты

Для работы с файловой системой в Rust используются два ключевых модуля:

  • std::fs — предоставляет функции для операций с файлами и папками (чтение, запись, удаление, копирование и т.д.).
  • std::path — содержит типы Path и PathBuf для работы с путями.

Для большинства задач достаточно добавить следующие импорты:

use std::fs;
use std::path::Path;
use std::io::prelude::*; // для работы с потоками

Работа с файлами: чтение и запись

Чтение содержимого файла

Самый простой способ прочитать весь файл в строку — использовать функцию fs::read_to_string. Она возвращает Result<String>, поэтому нужно обработать возможную ошибку.

use std::fs;

fn read_file(path: &str) -> Result<String, std::io::Error> {
    let content = fs::read_to_string(path)?;
    Ok(content)
}

fn main() {
    match read_file("example.txt") {
        Ok(text) => println!("Содержимое файла:\n{}", text),
        Err(e) => eprintln!("Ошибка чтения: {}", e),
    }
}

Если нужно прочитать файл как бинарные данные, используйте fs::read, который возвращает Vec<u8>.

Запись в файл

Для записи строки в файл применяется fs::write. Она создаст файл, если его нет, или перезапишет существующий.

use std::fs;

fn write_file(path: &str, content: &str) -> Result<(), std::io::Error> {
    fs::write(path, content)?;
    println!("Файл успешно записан");
    Ok(())
}

fn main() {
    let data = "Hello, Rust!\nВторая строка.";
    if let Err(e) = write_file("output.txt", data) {
        eprintln!("Ошибка записи: {}", e);
    }
}

Для дозаписи в конец файла или более тонкого контроля используйте OpenOptions:

use std::fs::OpenOptions;
use std::io::Write;

fn append_to_file(path: &str, line: &str) -> Result<(), std::io::Error> {
    let mut file = OpenOptions::new()
        .append(true)
        .create(true)
        .open(path)?;
    writeln!(file, "{}", line)?;
    Ok(())
}

Работа с директориями

Создание и удаление папок

Для создания директории используйте fs::create_dir (одна папка) или fs::create_dir_all (рекурсивно, как mkdir -p).

use std::fs;

fn main() -> Result<(), std::io::Error> {
    // Создание одной папки
    fs::create_dir("my_folder")?;

    // Рекурсивное создание вложенных папок
    fs::create_dir_all("data/subdir/logs")?;

    // Удаление пустой папки
    fs::remove_dir("my_folder")?;

    // Рекурсивное удаление папки со всем содержимым
    fs::remove_dir_all("data")?;

    Ok(())
}

Чтение содержимого директории

Функция fs::read_dir возвращает итератор по записям директории. Каждая запись — это DirEntry, из которой можно получить имя, тип и метаданные.

use std::fs;

fn list_directory(path: &str) -> Result<(), std::io::Error> {
    let entries = fs::read_dir(path)?;

    for entry in entries {
        let entry = entry?;
        let file_name = entry.file_name();
        let file_type = entry.file_type()?;

        if file_type.is_dir() {
            println!("[DIR]  {}", file_name.to_string_lossy());
        } else if file_type.is_file() {
            println!("[FILE] {}", file_name.to_string_lossy());
        }
    }
    Ok(())
}

fn main() {
    if let Err(e) = list_directory(".") {
        eprintln!("Ошибка: {}", e);
    }
}

Работа с путями: Path и PathBuf

В Rust пути представлены типами Path (неизменяемый, заимствованный) и PathBuf (собственный, изменяемый). Они кроссплатформенны и корректно обрабатывают разделители.

use std::path::Path;

fn main() {
    let path = Path::new("/home/user/docs/example.txt");

    // Получение компонентов пути
    println!("Родитель: {:?}", path.parent());
    println!("Имя файла: {:?}", path.file_name());
    println!("Расширение: {:?}", path.extension());
    println!("Без расширения: {:?}", path.file_stem());

    // Проверка существования
    println!("Существует: {}", path.exists());
    println!("Это файл: {}", path.is_file());
    println!("Это папка: {}", path.is_dir());

    // Сборка пути
    let mut new_path = path.to_path_buf();
    new_path.set_extension("md");
    println!("Новый путь: {}", new_path.display());
}

Для объединения путей используйте метод join:

let base = Path::new("/var/log");
let full = base.join("app.log");
// Результат: /var/log/app.log

Проверка существования и метаданные

Перед выполнением операций часто нужно проверить, существует ли файл или папка, а также получить метаданные (размер, время изменения, права доступа).

use std::fs;
use std::path::Path;

fn file_info(path: &str) ->                

Блоги

Рекомендаций по книгам