Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Запись сообщений об ошибках в стандартный поток ошибок вместо стандартного вывода

В данный момент весь наш вывод отправляется в терминал с помощью макроса println!. В большинстве терминалов существует два вида вывода: стандартный вывод (stdout) для общей информации и стандартная ошибка (stderr) для сообщений об ошибках. Это разделение позволяет пользователям направлять успешный вывод программы в файл, но при этом видеть сообщения об ошибках на экране.

Макрос println! может выводить данные только в стандартный вывод, поэтому для записи в стандартный поток ошибок нам нужно использовать что-то другое.

Проверка, куда записываются ошибки

Сначала посмотрим, как в текущем виде minigrep записывает свой вывод в стандартный вывод, включая сообщения об ошибках, которые мы хотим отправлять в стандартный поток ошибок. Мы сделаем это, перенаправив поток стандартного вывода в файл, при этом намеренно вызвав ошибку. Мы не будем перенаправлять стандартный поток ошибок, поэтому любой контент, отправленный в него, по-прежнему будет отображаться на экране.

Ожидается, что командные программы отправляют сообщения об ошибках в стандартный поток ошибок, чтобы мы могли видеть их на экране, даже если перенаправили стандартный вывод в файл. Наша программа сейчас ведёт себя неправильно: мы увидим, что сообщение об ошибке сохраняется в файл!

Чтобы продемонстрировать это поведение, запустим программу с помощью > и указанного пути к файлу _output.txt_, в который мы хотим перенаправить стандартный вывод. Мы не передадим никаких аргументов, что должно вызвать ошибку:

$ cargo run > output.txt

Синтаксис > указывает оболочке записывать содержимое стандартного вывода в _output.txt_ вместо экрана. Мы не увидели ожидавшееся сообщение об ошибке на экране, значит, оно оказалось в файле. Вот что содержит _output.txt_:

Problem parsing arguments: not enough arguments

Да, наше сообщение об ошибке выводится в стандартный вывод. Гораздо полезнее, чтобы подобные сообщения об ошибках выводились в стандартный поток ошибок, и в файл попадал только результат успешного выполнения. Мы это исправим.

Вывод ошибок в стандартный поток ошибок

Мы используем код из Листинга 12-24, чтобы изменить способ вывода сообщений об ошибках. Благодаря рефакторингу, который мы выполнили ранее в этой главе, весь код, выводящий сообщения об ошибках, находится в одной функции main. Стандартная библиотека предоставляет макрос eprintln!, который выводит данные в стандартный поток ошибок, поэтому давайте изменим два места, где мы вызывали println! для вывода ошибок, на eprintln!.

Filename: src/main.rs
use std::env;
use std::process;

use minigrep::Config;

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::build(&args).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {err}");
        process::exit(1);
    });

    if let Err(e) = minigrep::run(config) {
        eprintln!("Application error: {e}");
        process::exit(1);
    }
}
Listing 12-24: Запись сообщений об ошибках в стандартный поток ошибок вместо стандартного вывода с помощью eprintln!

Теперь запустим программу снова тем же способом, без аргументов и с перенаправлением стандартного вывода через >:

$ cargo run > output.txt
Problem parsing arguments: not enough arguments

Теперь мы видим ошибку на экране, а _output.txt_ остаётся пустым, что и ожидается от командной программы.

Запустим программу снова с аргументами, которые не вызывают ошибок, но всё равно перенаправим стандартный вывод в файл:

$ cargo run -- to poem.txt > output.txt

Мы не увидим никакого вывода в терминал, а _output.txt_ будет содержать наши результаты:

Имя файла: output.txt

Are you nobody, too?
How dreary to be somebody!

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

Резюме

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

Далее мы изучим некоторые возможности Rust, на которые повлияли функциональные языки: замыкания и итераторы.