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
相关产品推荐
相关产品推荐

