#错误码 #错误 #谷歌 #代码 #状态码 #枚举 #具体

编码

具有匹配谷歌“规范错误代码”的 ErrorKind 枚举的具体错误类型

1 个不稳定版本

0.1.0 2022年1月10日

#907Rust 模式

MIT/Apache

39KB
440

编码

这是一个具有匹配谷歌“规范错误代码”的 ErrorKind 枚举的具体错误类型。您可能从 谷歌云错误absl::StatusgRPC 状态码 中了解这些。

状态

错误代码枚举非常稳定。整体 crate API 正在开发中。欢迎提出想法!

我很快会将 Moonfire NVR 转换为这个。

示例

use coded::{bail, err, Error, ErrorBuilder, ErrorKind, ResultExt};

/// Reads application config from its well-known paths.
fn read_config() -> Result<MyConfig, Error> {
    for path in &PATHS {
        match read_json(p) {
            Ok(c) => return Ok(c),
            Err(e) if e.kind() == ErrorKind::NotFound => { /* keep trying */ },

            // The `bail!` macro is a convenient, flexible shorthand.
            Err(e) => bail!(e, msg("can't read {}", p.display()))
        }
    }

    // `bail!` lets us write `NotFound` without `use ErrorKind::*`.
    bail!(NotFound, msg("no config file at any of {:?}", PATHS))
}

/// Reads a JSON object from the given path.
/// 
/// This returns an `ErrorBuilder` rather than an `Error`, avoiding a redundant
/// entry in the error chain when it's wrapped by the caller.
fn read_json<T: Deserialize>(p: &Path) -> Result<(), ErrorBuilder> {
    // There's automatic conversion from std::io::Error to coded::ErrorBuilder which
    // selects an appropriate ErrorKind.
    let raw = std::fs::read(p)?;

    // ResultExt::err_kind wraps any std::error::Error impl, using the supplied
    // kind. It doesn't add a message.
    serde_json::from_str(&raw).err_kind(ErrorKind::InvalidArgument)
}

fn main() {
    if let Err(e) = inner_main() {
        // `Error::chain` prints not only `e` itself but also the full chain of sources.
        eprintln!("Fatal error:\n{}", e.chain());
        std::process::exit(1);
    }
}

fn inner_main() -> Result<(), Error> {
    let config = read_config()?;

    // ...
}

何时使用它?

  • 当您想要使用这个单一、通用、设计良好的错误代码枚举的优点时
    • 熟悉度:当您广泛使用相同的错误代码时,对其处理的期望是清晰的。
    • 监控:您可以使用这些代码有意义地聚合来自不同 API 的错误。
    • 稳定性:现有的错误代码及其编号永远不会改变。枚举被标记为 #[non_exhaustive],因为可能会添加新的代码,但自 2015 年以来尚未发生这种情况。这对于 RPC 或 crate 边界非常好。
    • gRPC 兼容性:许多服务(不仅限于谷歌)已经使用这些错误代码。
  • 当您想要您的错误强调调用者应该如何处理它们,而不是实现或依赖项的细节时。请参阅博客文章 Rust 错误处理
  • 当返回 Ok 必须便宜时。一个 Result<(), coded::Error> 是一个单词,因此返回 Ok 比使用更大的错误类型要快[9]
  • 当您希望与代码、详细信息、完整的错误链等丰富的可读性错误消息一起使用时。目前,“更多”可以是堆栈跟踪(由应用程序的 Cargo.toml 和环境变量控制)。在将来,也许会有 tracing_error::SpanTrace 以及/或任意有效负载。
  • 当您想使用 err!bail! 宏轻松地返回错误时。

我什么时候不应该使用它?

  • 当您根本不在乎错误代码时。您可能对 anyhoweyresnafu::Whatever 更感兴趣。
  • 当您需要现在就有一个绝对稳定的错误类型时。如上所述,实际的枚举值不会改变,但 coded 的 API 到达 1.0 还有一点早。(注意:如果有需求,我可以将绝对稳定的 coded::{ErrorKind, ToErrorKind} 类型拆分成自己的 crate。然后您可以通过在您自己的 crate 的公共 Error 类型中包装 coded::Error 来有一个稳定的错误类型。)
  • 当您需要具有自定义字段以指导调用者处理特定领域错误的详尽枚举时,有时以 API 稳定性为代价。
  • 当返回 Err 必须便宜时。 coded::Error 不便宜:它需要堆分配,并且目前无法为特定的库或调用点禁用堆栈跟踪。
  • 当您只想使用 ? 将其他 crate 的错误传递出去,而不必围绕这些错误类型和/或 coded 创建自己的包装。由于 Rust 的孤儿规则coded::ToErrorKind 只能在错误定义的地方或 coded 中实现。这限制了可用性。(一旦专业化稳定,? 可以 通过 ErrorKind::Unknown 传递其他类型,但这可能比有帮助更多。同样,我们可以用类似 inventory 的方法来与孤儿规则作斗争,但我们可能不应该。)

在关键正确性 Rust 项目中的错误处理 描述了这些如何适用于 sled 数据库。

如果您需要自己的错误类型但又讨厌编写样板代码,请尝试 thiserrorsnafu 的 derive 宏。

我应该怎么使用它?

返回 coded::Error。使用注释来记录API在特定情况下返回的错误类型。可以自由地添加额外的错误类型,无需打破semver,因为调用者必须非穷尽性地匹配。

缺少什么?

  • 扩展状态以支持类型化有效载荷的能力,就像absl::Status支持的那样。我希望使用内置于std::error::Error trait中的支持(参见RFC 2895),但该功能尚未存在。《code>coded可能在此期间增长自己的API来支持此功能。
  • 支持将数据序列化和反序列化为protobufs。至少有三个Rust protobuf库(prostprotobuf,和quick-protobuf)。我们可以通过Cargo功能标志支持每个库。

许可证

Apache 2.0或MIT,任选其一。

作者

Scott Lamb <[email protected]>

依赖

~0–540KB
~11K SLoC