class Failure is Nil { }

Failure 是一个未抛出Exception,通常通过调用 &fail 生成。它充当 Exception 对象的包装器。

Sink(void)上下文会导致 Failure 抛出,即变成常规异常。use fatal 语用程序会在语用程序作用域内的所有上下文中导致这种情况发生。在 try 内,use fatal 会自动设置,并且你可以使用 no fatal禁用它。

这意味着 Failures 通常仅在通常会产生右值的代码的情况下才有用;在经常在 sink 上下文中调用的代码中,Failures 与 Exceptions 几乎等效(即,对于其副作用,例如使用 say)。

类似地,你通常应该仅在通常预期返回某些内容的代码内使用 &fail

检查 Failure 是否为真(使用 Bool 方法)或是否已定义(使用 defined 方法)会将失败标记为已处理,并导致它不再在 sink 上下文中抛出。

你可以调用 handled 方法来检查失败是否已处理。

对未处理的失败调用方法会传播失败。规范规定结果是另一个 Failure;在 Rakudo 中,它会导致失败抛出。

因为 Failure 是 Nil,它是未定义的,所以安全执行可能失败的代码的常用习惯用法是使用 with/else 语句

sub may_fail--> Numeric:D ) {
  my $value = (^10).pick || fail "Zero is unacceptable";
  fail "Odd is also not okay" if $value % 2;
  return $value;
}
 
with may_fail() -> $value { # defined, so didn't fail 
  say "I know $value isn't zero or odd."
} else { # undefined, so failed, and the Failure is the topic 
  say "Uh-oh: {.exception.message}."
}

方法§

方法 new§

multi method new(Failure:D:)
multi method new(Failure:U:)
multi method new(Failure:U: Exception:D \exception)
multi method new(Failure:U: $payload)
multi method new(Failure:U: |cap (*@msg))

返回一个新的 Failure 实例,其有效负载作为参数给出。如果在 Failure 对象上不带参数调用它,它将抛出;在类型值上,它将创建一个没有有效负载的空 Failure。后者可以是 ExceptionException 的有效负载。典型的有效负载将是一个带有错误消息的 Str。还接受有效负载列表。

my $e = Failure.new(now.DateTime'WELP‼');
say $e;
CATCH{ default { say .^name''.Str } }
# OUTPUT: «X::AdHoc: 2017-09-10T11:56:05.477237ZWELP‼␤»

方法 handled§

method handled(Failure:D: --> Bool:Dis rw

对于已处理的失败返回 True,否则返回 False

sub f() { fail }my $v = fsay $v.handled# OUTPUT: «False␤»

handled 方法是一个 左值,请参见 例程特性 is rw,这意味着你还可以使用它来设置已处理状态

sub f() { fail }
my $v = f;
$v.handled = True;
say $v.handled# OUTPUT: «True␤»

方法 exception§

method exception(Failure:D: --> Exception)

返回失败包装的 Exception 对象。

sub failer() { fail };
my $failure = failer;
my $ex = $failure.exception;
put "$ex.^name(): $ex";
# OUTPUT: «X::AdHoc: Failed␤»

方法 self§

method self(Failure:D: --> Failure:D)

如果调用者是 已处理Failure,则按原样返回它。如果没有处理,则抛出其 Exception。由于 Mu 类型 提供 .self 适用于每个类,调用此方法是爆炸性地过滤掉 Failures 的一种便捷方法

my $num1 = ''.Int;
# $num1 now contains a Failure object, which may not be desirable 
 
my $num2 = ''.Int.self;
# .self method call on Failure causes an exception to be thrown 
 
my $num3 = '42'.Int.self;
# Int type has a .self method, so here $num3 has `42` in it 
 
(my $stuff = ''.Int).so;
say $stuff.self# OUTPUT: «(HANDLED) Cannot convert string to number…» 
# Here, Failure is handled, so .self just returns it as is

方法 Bool§

multi method Bool(Failure:D: --> Bool:D)

返回 False,并将失败标记为已处理。

sub f() { fail };
my $v = f;
say $v.handled# OUTPUT: «False␤» 
say $v.Bool;    # OUTPUT: «False␤» 
say $v.handled# OUTPUT: «True␤»

方法 Capture§

method Capture()

如果调用者是类型对象或 已处理的 Failure,则抛出 X::Cannot::Capture。否则,抛出调用者的 异常

方法 defined§

multi method defined(Failure:D: --> Bool:D)

返回 False(失败在官方上是未定义的),并将失败标记为已处理。

sub f() { fail };
my $v = f;
say $v.handled# OUTPUT: «False␤» 
say $v.defined# OUTPUT: «False␤» 
say $v.handled# OUTPUT: «True␤»

方法 list§

multi method list(Failure:D:)

将失败标记为已处理,并抛出调用者的 异常

子例程 fail§

multi fail(--> Nil)
multi fail(*@text)
multi fail(Exception:U $e  --> Nil )
multi fail($payload --> Nil)
multi fail(|cap (*@msg--> Nil)
multi fail(Failure:U $f --> Nil)
multi fail(Failure:D $fail --> Nil)

退出调用 Routine,并返回一个 Failure 对象,该对象包装异常 $e - 或对于 cap$payload 形式,一个由 @text 拼接构成的 X::AdHoc 异常。如果调用者通过 pragma use fatal; 激活了致命异常,则抛出异常,而不是将其作为 Failure 返回。

# A custom exception defined 
class ForbiddenDirectory is Exception {
    has Str $.name;
 
    method message { "This directory is forbidden: '$!name'" }
}
 
sub copy-directory-tree ($dir{
    # We don't allow for non-directories to be copied 
    fail "$dir is not a directory" if !$dir.IO.d;
    # We don't allow 'foo' directory to be copied too 
    fail ForbiddenDirectory.new(:name($dir)) if $dir eq 'foo';
    # or above can be written in method form as: 
    # ForbiddenDirectory.new(:name($dir)).fail if $dir eq 'foo'; 
    # Do some actual copying here 
    ...
}
 
# A Failure with X::AdHoc exception object is returned and 
# assigned, so no throwing Would be thrown without an assignment 
my $result = copy-directory-tree("cat.jpg");
say $result.exception# OUTPUT: «cat.jpg is not a directory␤» 
 
# A Failure with a custom Exception object is returned 
$result = copy-directory-tree('foo');
say $result.exception# OUTPUT: «This directory is forbidden: 'foo'␤»

如果使用通用 Failure 调用它,则抛出临时未定义失败;如果它是一个已定义的 Failure,它将被标记为未处理。

sub re-fail {
    my $x = +"a";
    unless $x.defined {
        $x.handled = True;
        say "Something has failed in \$x "$x.^name;
        # OUTPUT: «Something has failed in $x Failure␤» 
        fail($x);
        return $x;
    }
}
 
my $x = re-fail;
say $x.handled# OUTPUT: «False␤» 

类型图§

Failure 的类型关系
raku-type-graph Failure Failure Nil Nil Failure->Nil Mu Mu Any Any Any->Mu Cool Cool Cool->Any Nil->Cool

展开上面的图表