在CATCH块中区分异常和失败[RAKU]


9

我们知道,可以通过CATCH块来处理故障。

在下面的示例中,我们在“ other-sub”中创建一个“ AdHoc”失败,并在CATCH块中(在“ my-sub”中)处理异常。

sub my-sub {
    try {
        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;
    }
}

sub other-sub { fail 'Failure_X' }

my-sub();

输出如下:

AdHoc Exception handled here
This was a Failure

我的问题是:如何区分CATCH块中的失败和“正常”异常,以便区分这两种情况?

Answers:


12

之间的关系Failure,并Exception是一个Failure有一个Exception-也就是说,它拥有异常对象作为其状态的一部分。像这样:

class Failure {
    has Exception $.exception;
    # ...
}

Failure“爆炸”时,它会通过将其Exception内部的抛出而爆炸。因此,到达CATCH块的是Exception对象,没有链接返回到封闭的Failure。(实际上,一个给定的Exception对象原则上可以由许多人持有Failure。)

因此,没有直接的方法可以检测到这一点。从设计的角度来看,您可能不应该这样做,应该找到其他解决问题的方法。A Failure只是一种延迟抛出异常并将其视为值的方式;这并不意味着根本问题的性质会发生变化,因为它是作为一个值而不是作为控制流的立即传递来传达的。不幸的是,最初的目标并未在问题中阐明。您可能会发现查看控制异常很有用,但否则可能会发布另一个有关您要解决的基本问题的问题。可能有更好的方法。

为了完整起见,我将指出,有一些间接方法可以检测到Exception抛出了Failure。例如,如果获取.backtrace异常对象的并查看顶部框架的包,则可以确定它来自Failure

sub foo() { fail X::AdHoc.new(message => "foo") }
try {
    foo();
    CATCH {
        note do { no fatal; .backtrace[0].code.package ~~ Failure };
        .resume
    }
}

但是,这在很大程度上取决于可以轻松更改的实现细节,因此我不会依赖它。


为了澄清问题,我的目的是仅处理异常(在CATCH块中)。如果发生故障,我想恢复原状,好像什么都没发生,然后让其余代码(在CATCH之外)处理故障。在我的示例中,我没想到返回的Failure会触发所包含的Exception!我所做的就是将结果存入$ b并将其作为Bool进行检查。在我看来,这并不构成对失败的“使用”,因此并不构成对CATCH块的触发!取而代之的是,CATCH始终在处理Failure中包含的异常!
雅加尔

此外,在您的示例中,关于间接检测故障的方法,返回的Bool(通过智能检查故障类型)的值为“ False”。但我希望它是“真实的”!我错过了什么???
雅加尔

1
@jakar try块表示use fatal编译指示,这意味着Failure在该块中进行的调用返回的任何内容都会立即转换为异常。只是不使用try; 一个CATCH可以进入Raku的任何街区(因此只需将其放在sub)。或者,no fatal在代码try块的顶部写。
乔纳森·沃辛顿

那我的第二条评论呢?
雅加尔

1
运行我打印的示例 True我在本地拥有的Rakudo版本上。如果您不满意,那就证明了这样做的脆弱性。
乔纳森·沃辛顿

6

只需移除try包装器即可:

sub my-sub {

#    try {              <--- remove this line...

        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;

#    }                  <--- ...and this one

}

sub other-sub { fail 'Failure_X' }

my-sub();

您曾经使用过try。A try做一些事情,但是这里的相关事情是,它告诉Raku立即Failure将范围内的任何s 提升为例外-这就是您所说的想要的。因此,最简单的解决方案是停止这样做。


这个答案只是冗长地重复了jnthn的部分解释(请参见他在答案下方写的特别评论)。但是我并没有说服所有读者都能理解/理解这一方面,也不认为对jnthn的答案发表一两个评论会有所帮助,因此也就无法回答。

我将其写为社区答复,以确保我不会从任何投票中受益,因为这显然并不能保证。如果投票足够,我们将其删除。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.