Shanraq.org Shanraq.org
Чтение и запись файлов
IT

Rust: с нуля до своего органайзера Урок 50 из 55

Чтение и запись файлов

Урок 50. Записать текст, прочитать его и объяснить отказ ввода-вывода.

Текст переведён с помощью ИИ.

Предварительно: Result, передача ошибки и пути. Это отдельный учебный файл во временной папке, не данные настоящего органайзера.

Образ и опорная карта

Запись в тетрадь может не состояться: тетрадь не нашли, к ней нет доступа или текст испорчен. Ввод-вывод (I/O) — работа программы с внешним миром, в том числе с файлами. Result сообщает успех или конкретную ошибку. Предел образа: файл может измениться между проверкой и чтением, поэтому наличие пути заранее не гарантирует успех последующей операции.

выбрать отдельный временный путь → создать без перезаписи → записать байты → закрыть → прочитать UTF-8 → удалить → обработать ошибку. temp_dir() даёт системную временную папку; process::id() добавляет номер текущего процесса к учебному имени. create_new(true) требует нового файла и не перезапишет существующий. write_all — метод трейта Write, поэтому его импортируют; он пытается записать все байты текста. as_bytes() переводит UTF-8 строку в байты без потери. drop(file) закрывает открытый файл до чтения, что важно и для Windows. read_to_string читает весь файл как UTF-8: отсутствие файла, отказ доступа и неверный UTF-8 дают Err. ? передаёт ошибку из run, а std::io::Result<()> означает успех без дополнительного значения. eprintln! печатает ошибку отдельно от обычного результата. ErrorKind обозначает вид ошибки; NotFound значит, что файл не найден.

std::process::exit(1) завершает программу с кодом 1 при неустранённой ошибке; код 0 означал бы успех и ввёл бы вызывающую программу в заблуждение.

Сохраните и снова прочитайте учебную строку

На телефоне длинную строку кода можно сдвинуть пальцем внутри светлой рамки; вся страница при этом не расширяется.

use std::fs::{self, OpenOptions};
use std::io::Write;

fn run() -> std::io::Result<()> {
    let path = std::env::temp_dir().join(format!("rust-lesson-50-{}.txt", std::process::id()));
    let mut file = OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(&path)?;
    file.write_all("Купить книгу\n".as_bytes())?;
    drop(file);
    let text = fs::read_to_string(&path)?;
    print!("{text}");
    fs::remove_file(&path)?;
    Ok(())
}

fn main() {
    if let Err(error) = run() {
        eprintln!("Ошибка файла: {error}");
        std::process::exit(1);
    }
}
Купить книгу

После чтения remove_file удаляет только этот учебный файл. При ошибке до удаления он может остаться во временной папке; приложение должно учитывать такой случай. create_new при следующем совпадении имени вернёт ошибку и не затрёт старое содержимое. Здесь нет постоянного хранения органайзера: безопасное сохранение и восстановление будут позже.

Вопросы для воспроизведения

  1. Чем create_new(true) защищает данные?
  2. Почему read_to_string может вернуть Err даже при существующем файле?
  3. Где будет видна ошибка из eprintln!?

Задание

Обязательное. После удаления попробуйте fs::read_to_string(&path) ещё раз. Через match и ErrorKind::NotFound выведите Файл отсутствует; для неожиданных успеха и ошибки предусмотрите отдельные ветви. Сначала предскажите результат.

Ответы

Показать ответы

Добавьте use std::io::{ErrorKind, Write};. После remove_file сравните error.kind() с ErrorKind::NotFound; не сравнивайте текст сообщения ОС, он зависит от языка и системы.

use std::fs::{self, OpenOptions};
use std::io::{ErrorKind, Write};

fn run() -> std::io::Result<()> {
    let path = std::env::temp_dir().join(format!("rust-lesson-50-{}.txt", std::process::id()));
    let mut file = OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(&path)?;
    file.write_all("Купить книгу\n".as_bytes())?;
    drop(file);
    let text = fs::read_to_string(&path)?;
    print!("{text}");
    fs::remove_file(&path)?;
    match fs::read_to_string(&path) {
        Err(error) if error.kind() == ErrorKind::NotFound => println!("Файл отсутствует"),
        Ok(_) => println!("Неожиданный успех"),
        Err(error) => eprintln!("Ошибка файла: {error}"),
    }
    Ok(())
}

fn main() {
    if let Err(error) = run() {
        eprintln!("Ошибка файла: {error}");
        std::process::exit(1);
    }
}
Купить книгу
Файл отсутствует

После проверки

Если файл недоступен, error.kind() может быть PermissionDenied; если байты не образуют UTF-8, read_to_string тоже вернёт ошибку. Не показывайте такие случаи как пустой список задач. Для настоящих данных нужно ещё защитить запись от обрыва и сохранить копию: следующие уроки разбирают это отдельно. Официальная документация read_to_string.

Предыдущий урок · Оглавление

Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом

Комментарии (0)

Пока нет комментариев. Будьте первым.