Почему пользовательское перечисление ошибок Rust требует реализации Display, когда мне все равно нужно форматировать вывод?

У меня есть собственное перечисление ошибок, которое включает в себя пару распространенных ошибок в моем коде:

pub enum ParseError {
    Io(io::Error),
    Parse(serde_json::error::Error),
    FileNotFound(PathBuf, io::Error),
}

Это перечисление реализует трейт Display (по мере необходимости):

impl Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ParseError::Io(io_error) => write!(f, "{}", io_error),
            ParseError::Parse(parse_error) => write!(f, "{}", parse_error),
            ParseError::FileNotFound(file, err) => {
                write!(f, "Could not open file {}: {}", file.to_string_lossy(), err)
            }
        }
    }
}

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

    match my_function(arg1, arg2) {
        Ok(_) => (),
        Err(error) => match error {
            Io(err) => //do stuff,
            },
            Parse(err) => //do stuff
            },
            FileNotFound(file, err) => {
                println!("Can't find file '{}': {}", file.to_string_lossy(), err)
            }

Без форматирования имени файла и ошибки в Match, Rust просто печатает общую ошибку (в данном случае «Система не может найти указанный файл. (Ошибка ОС 2)».

Мой вопрос: почему требуется сначала реализовать отображение и форматирование ошибки, если требуется отформатировать ее снова, чтобы распечатать/использовать ее?

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Повторно форматировать не требуется. Это особенно полезно, когда вы не используете match на нем, но когда вы это делаете, вы можете использовать @ для создания привязки ко всему элементу:

match my_function(arg1, arg2) {
    Ok(_) => (),
    Err(error) => match error {
        Io(err) => { //do stuff,
        }
        Parse(err) => { //do stuff
        }
        error @ FileNotFound(..) => {
            println!("{error}");
        }
    },
}

Вы также можете создать ошибку на месте:

FileNotFound(file, err) => {
    println!("{}", FileNotFound(file, err));
}

Еще одна распространенная вещь — использовать всеобъемлющее действие для выполнения действия над всеми несопоставленными ошибками:

error => {
    println!("{error}");
}

Другие вопросы по теме