#stack-trace #error #tracing #return

无需std ertrace

实验性Rust错误返回跟踪

3个不稳定版本

0.2.1 2020年4月6日
0.2.0 2020年4月6日
0.1.0 2020年3月30日

#807 in 调试

MIT/Apache

25KB
319

Ertrace

实验性Rust错误返回跟踪。

该库的即时目标是:1. 提供基于错误返回跟踪的最小样板错误处理故事,2. 通过展示错误返回跟踪的价值,希望将其直接集成到Rust编译器中。

该库仍处于早期阶段,非常不稳定。已经尽力实现具有性能的功能,但到目前为止,尚未进行性能分析。肯定还有改进的空间。

错误返回跟踪

错误返回跟踪是由Andrew Kelley为Zig编程语言开发的一种新颖的错误处理概念。错误返回跟踪看起来有点像许多流行编程语言在异常未被捕获时显示的堆栈跟踪。堆栈跟踪提供了用于确定错误来源的极其有价值的信息,但遗憾的是,它们具有相当大的性能成本。因此,Rust仅启用堆栈跟踪以处理panic,并且仅当定义了RUST_BACKTRACE环境变量时。

错误返回跟踪提供与堆栈跟踪类似的信息,但性能成本要小得多。它们通过在调用栈中跟踪错误上升,而不是在遇到错误时捕获整个堆栈跟踪来实现这一点。(有关堆栈跟踪和错误返回跟踪之间性能差异的更多信息,请参阅下面的性能部分。)

此外,错误返回跟踪甚至可以提供比基本堆栈跟踪更有用的信息,因为它们跟踪一个类型的错误导致另一个类型错误的原因和位置。最后,由于错误是通过每个返回点跟踪的,因此错误返回跟踪与M:N线程、future和async/await无缝工作。

示例

use ertrace::{ertrace, Ertrace};

fn main() -> Result<(), AError> {
    // Forward any `AError` errors from `a`.
    a().map_err(|mut e| ertrace!(e =>))
}

fn a() -> Result<(), AError> {
    // On any error in `b`, return an `AError`, and trace the cause.
    b().map_err(|e| ertrace!(e => AError))?;
    Ok(())
}

fn b() -> Result<(), BError> {
    // Forward any `BError` errors from `b_inner`.
    b_inner().map_err(|mut e| ertrace!(e =>))
}

fn b_inner() -> Result<(), BError> {
    if true {
        // Initialize the traced error struct, `BError1`, and then use the `?`
        // operator to convert it into the appropriate `BError` enum instance
        // and return it.
        Err(ertrace!(BError1))?
    } else {
        // Initialize the traced error struct, `BError2`, and then use the `?`
        // operator to convert it into the appropriate `BError` enum instance
        // and return it.
        Err(ertrace!(BError2))?
    }
}

ertrace::new_error_types! {
    // Define new traced error structs `AError`, `BError1`, and `BError2`.
    pub struct AError(Ertrace);
    pub struct BError1(Ertrace);
    pub struct BError2(Ertrace);

    // Define a new traced error enum `BError`, with variants for
    // `BError1` and `BError2`.
    pub enum BError {
        BError1(BError1),
        BError2(BError2),
    }
}

输出

Error: AError
error return trace:
    0: BError1 at examples/basics.rs:24:13 in basics
    1: => at examples/basics.rs:16:31 in basics
    2: AError at examples/basics.rs:10:21 in basics
    3: => at examples/basics.rs:5:25 in basics

no_std 支持

Ertrace提供no_std支持。默认情况下,它依赖于stdcrate,为了提供额外的功能,但这个依赖项被stdfeature限制,可以通过在Cargo依赖项中指定default-features = false来禁用它。

目前需要alloc crate,但通过指定一个用于存储错误跟踪的静态内存块,可以轻松地移除这个需求。如果您有这个需求,请在Github上创建一个issue

性能:堆栈跟踪与错误返回跟踪

为了在未捕获的异常发生时显示堆栈跟踪,必须在异常创建时(或抛出/引发时)捕获整个堆栈跟踪。这是一个相对昂贵的操作,因为它需要遍历每个堆栈帧,并且至少为调用堆栈中的每个函数存储一个指针,通常在某个堆分配的线程局部存储中。通常认为,异常只应在异常情况下抛出,因此收集堆栈跟踪的性能成本不会显著降低程序的整体性能。然而,在现实中,错误相当常见,堆栈跟踪的成本不容忽视。

相比之下,错误返回跟踪的成本开始非常小,并且与错误返回的次数线性增长。如果错误在首次创建的堆栈帧之上一个堆栈帧中被处理,那么运行时开销可以小到只有几个ALU操作和一个内存写入(如果您有编译器支持的话...这个库实现的运行时开销略高)。

无运行时依赖

特性