Rust:如何覆盖默认的报错方式?

Rust: how to override the default way of reporting error?

当 Rust 中发生错误时,大多数 Rust 标准和第三方库(例如无论如何)只会将错误消息打印到 stdout/stderr。我需要的是用我自己的函数覆盖它。例如,用位置信息调用Windows API MessageBox(哪个文件和行会崩溃)。

到目前为止,我尝试了几种方法,每一种都缺少一些东西。

  1. 写下我的自定义错误,并覆盖它的 Debug 特性。问题是这个错误类型需要能够包装任何潜在的错误(例如 dyn std::error::Error),这样我仍然可以写
fn test() -> std::result::Result<(), CustomError> {
  ...
  if (has_windows_error) {
    return Err(WindowsError(123))?;
  }
  if (has_io_error) {
    return Err(IoError(123))?;
  }

  Ok(())
}

然而,这意味着 CustomError 需要同时实现 std::error::ErrorFrom<std::error::Error>,这两者相互冲突。

  1. 编写我的自定义结果类型,并实现Try。这种方法需要接触实验 #![feature(try_trait_v2)].

  2. 使用std::panic::set_panic_hook(),然后永远不要在代码中使用?。相反,仅立即 unwrap() 所有 Result。这是必需的,因为如果我允许错误出现,我将丢失 PanicInfo.

    中的位置信息

我对 Rust 比较陌生,所以我可能遗漏了很多东西。选项 3 是我目前正在使用的,尽管我不喜欢它如何迫使我放弃惯用的编码风格。

目前实现自定义错误输出的最简洁、侵入性较小的方法是什么?

选项 3 肯定不是这里的方法。在 Rust 中,panic 意味着不可恢复的错误,根据我过去 2 年在社区中的经验,这条准则得到了相当认真的对待。此外,选项 1 和 2 添加了相当大的 side-effects 操作,而这些操作从来都不是为了隐藏这样的东西。

此外,我不确定您所说的“第 3 方库(例如,无论如何)只会将错误消息打印到 stdout/stderr”是什么意思。如果你的函数 returns 一个 anyhow::Result<T> 并且发生错误,那么发生的只是错误被转换为 anyhow::Error,除非你打印它,否则它不会被打印出来。

底线是 Rust 中没有“错误处理框架”。 Results 一点也不特别,事实上我鼓励你阅读 the source code. If you want to handle a result in a certain way, then match on it and write the code to handle it. Moreover the panic handling machinery is not meant to elevate them to something like C++ exceptions. You can read more about the motivations for it here

听起来最适合你的方法是使用 anyhow 或类似的东西,在你的程序中的某些时候你可以匹配结果,转换 anyhow::Error (或 std::error::Error 你选择的实现者)到一个字符串并打开一个消息框。如果您想包含有关错误发生位置的文件和行信息,则可以通过标准库提供的 file! and line! 宏将其添加到错误的 message/data 中。这可能类似于以下内容:

pub fn main() {
    if let Err(error) = try_main() {
        // show_message_box implementation not shown
        show_message_box(error.to_string());
    }
}

pub fn try_main() -> anyhow::Result<()> {
    // code which makes extensive use of `?`
}

请注意,Rust 的错误处理方法与许多其他广泛使用的语言不同,因此需要改变编码风格。错误是故意侵入你的代码,所以你实际上正确地处理了它们,这是我随着时间的推移而非常感激的事情。事实上,Rust 的特质使它成为一种非常固执己见的语言,所以当你遇到痛点时,我建议不要尝试使用超出预期用途的语言特性来解决它们,因为这比解决你的问题更有可能导致额外的痛点。