在Rust中,当Assert失败时,如何以消耗局部变量的方式打印数据?

ldioqlga  于 2023-02-08  发布在  其他
关注(0)|答案(2)|浏览(130)

我有一些测试,其中有一些变量保存一些重要的数据,我想在Assert失败时打印它们的数据。获取我需要的数据消耗变量,所以打印代码必须拥有变量。在这个例子中,我想在Assert失败时调用dump_foo_data

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);
}

我可以通过使dump_foo_data不返回和panic来得到一个非常糟糕的解决方案:

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

然后,我使用if检查故障,而不是使用assert!,并可能调用dump_foo_data

let success = true;
    if !success {
        dump_foo_data(f);
    }

这是太多的代码行,我需要指定f。实际上,我有不止一个像f这样的变量需要从中转储数据,所以在每次检查中列出一个相关的局部变量不是很好。
我不知道如何编写一个宏来使它更好,因为我仍然需要将每个相关的局部变量传递给宏。
我也想不出使用std::panic的方法。update_hook需要获得f的所有权,然后我就不能在测试中使用它。
在拉斯特有什么好办法吗?
编辑:我想到了另一种方法:把每个相关的局部变量放到一个Rc中,然后把每个局部变量传递给std::panic::update_hook。我还没有确认这是否有效。
编辑2:也许我可以滥用break来做我在评论中用goto解释的事情。

9q78igpj

9q78igpj1#

一种不使用任何宏或共享内部可变性引用魔法的方法可能是重新占用f

fn check_or_dump(success: bool, f: Foo) -> Foo {
   match success {
     true => f,
     false => panic!("Behold foo data: {:?}", dump_foo_data(f)),
   }
}

您可以这样使用它:

let f = Foo();
let success = true;
let f = check_or_dump(success, f);
let success = false;
let f = check_or_dump(success, f);
// and so on.
1dkrff03

1dkrff032#

这里有一个解决方案,它没有宏或内部可变性,也不需要列出每个检查的所有变量,它的灵感来自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");
    }
}

Playground

相关问题