You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中断言失败时如何消费局部变量并打印其数据?

解决方案

方案一:用Rc<RefCell<T>>配合panic钩子

把需要打印的变量包装成Rc<RefCell<T>>,既可以在测试中正常使用,又能在panic钩子中通过克隆Rc获取数据(无需转移所有权)。测试结束后记得恢复原钩子,避免影响其他测试用例。

use std::cell::RefCell;
use std::panic;
use std::rc::Rc;

struct Foo();

fn dump_foo_data(f: &Foo) {
    eprintln!("Behold, Foo data: ");
}

#[test]
fn my_test() {
    let f = Rc::new(RefCell::new(Foo()));
    let f_hook = Rc::clone(&f);

    // 保存原panic钩子
    let original_hook = panic::take_hook();
    panic::set_hook(Box::new(move |panic_info| {
        // 打印数据,通过RefCell获取引用
        dump_foo_data(&f_hook.borrow());
        // 调用原钩子完成panic处理
        original_hook(panic_info);
    }));

    eprintln!("begin");

    // 测试逻辑中正常使用变量
    &f.borrow();
    let success = true;
    assert!(success);

    &f.borrow();
    let success = false;
    assert!(success);

    // 测试结束恢复原钩子
    panic::set_hook(original_hook);
}

这个方法无需修改断言逻辑,所有断言失败都会自动触发数据打印,适合多变量场景——只需把所有需要打印的变量用Rc<RefCell>包装并传给钩子即可。

方案二:自定义断言宏+作用域模拟失败跳转

利用Rust的块作用域和break,把测试逻辑放在块内,断言失败时直接跳转到块末尾统一处理变量打印和panic。无需每次断言手动传变量,因为变量处于同一作用域。

struct Foo();

fn dump_foo_data(f: Foo) {
    eprintln!("Behold, Foo data: ");
}

// 自定义断言宏,失败时打印变量并panic
macro_rules! assert_or_dump {
    ($cond:expr, $($vars:expr),*) => {
        if !$cond {
            // 一次性消费所有需要打印的变量
            $(dump_foo_data($vars);)*
            panic!("Assertion failed");
        }
    };
}

#[test]
fn my_test() {
    let f = Foo();
    eprintln!("begin");

    // 用loop块包裹测试逻辑,失败时直接执行后续处理
    let _ = loop {
        // 第一个测试
        &f;
        let success = true;
        assert_or_dump!(success, f);

        // 第二个测试
        &f;
        let success = false;
        assert_or_dump!(success, f);

        break;
    };
}

如果有多个变量,只需在宏调用时添加变量即可,比如assert_or_dump!(success, f, bar, baz),宏会自动依次调用打印函数。

方案三:用catch_unwind捕获panic后处理

通过std::panic::catch_unwind捕获断言的panic,在捕获后处理变量打印,再重新抛出panic。此方法无需修改变量包装,但要注意catch_unwind仅能捕获实现UnwindSafe的类型,测试场景下大部分类型都满足要求。

use std::panic;

struct Foo();

fn dump_foo_data(f: Foo) {
    eprintln!("Behold, Foo data: ");
}

#[test]
fn my_test() {
    let mut f = Foo();
    eprintln!("begin");

    // 第一个测试
    &f;
    let success = true;
    if let Err(_) = panic::catch_unwind(|| assert!(success)) {
        dump_foo_data(f);
        panic!("Assertion failed");
    }

    // 第二个测试
    &f;
    let success = false;
    if let Err(_) = panic::catch_unwind(|| assert!(success)) {
        dump_foo_data(f);
        panic!("Assertion failed");
    }
}

该方法无需修改断言本身,但每次测试都要套一层catch_unwind,适合变量数量较少的场景。


内容的提问来源于stack exchange,提问作者jrpear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:50:23