I have some tests which have some variables that hold some important data and I'd like to print their data when an assertion fails. Getting the data I need consumes the variables, so the printing code must own the variables. In this example, I'd want to call dump_foo_data
once an assertion fails:
struct Foo();
fn dump_foo_data(f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
eprintln!("begin");
// do a test
&f;
let success = true;
assert!(success);
// do another test
&f;
let success = false;
assert!(success);
}
I can make a very bad solution by making dump_foo_data
non-returning and panic:
fn dump_foo_data(f: Foo) -> ! {
eprintln!("Behold, Foo data: ");
panic!();
}
Then instead of using assert!
, I check the failure with an if
and maybe call dump_foo_data
:
let success = true;
if !success {
dump_foo_data(f);
}
This is too many lines of code, and I need to specify f
. In reality, I have more than one variable like f
that I need to dump data from, so it's not very nice to list out single relevant local variable in every check.
I couldn't figure out how to write a macro to make this better because I'd still need to pass every relevant local variable to the macro.
I couldn't think of a way to use std::panic
either. update_hook
would need to take ownership of f
, then I couldn't use it in tests.
Is there any good way to do this in Rust?
Edit: I've thought of another approach: put each relevant local in an Rc
then pass each of those to std::panic::update_hook
. I've not confirmed whether this'll work yet.
Edit 2: Maybe I could abuse break
to do what I explained with goto
in a comment.
Here's a solution without macro or interior mutability and that doesn't require you to list all the variables on each check. It is inspired by this answer:
struct Foo();
fn dump_foo_data(_f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
let doit = || -> Option<()> {
eprintln!("begin");
// do a test
&f;
let success = true;
success.then_some(())?;
// do another test
&f;
let success = false;
success.then_some(())?;
Some(())
};
if let None = doit() {
dump_foo_data (f);
panic!("Test failure");
}
}