首页 > 解决方案 > 为什么“尝试”不会导致未定义的子程序错误?

问题描述

有几次我遇到了忘记Try::Tiny在脚本中加载模块但我仍然使用它的try-catch块的情况,如下所示:

#!/usr/bin/env perl

use strict; 
use warnings;

try {
  call_a( 'x' );
} catch {
  die "ACTUALLY die $_";
};


sub call_a {
  die "Yes, I will";
}

出于某种原因,该脚本工作正常,没有给出任何提示没有try. 没有Undefined subroutine错误。这让我想知道为什么我提出的异常没有被捕获。

为什么这会默默地工作,没有错误?

编辑

我也查看了符号表:

say "$_: %main::{ $_ }" for keys %main::; 

发现没有try。我也尝试按照main::try上面的脚本调用它,它也没有导致错误。

标签: perl

解决方案


这是由于间接对象语法,并且是此示例的更精细的变体。

间接对象表示法”允许代码

PackageName->method(@args);

写成

method PackageName @args;

所以“try”和“catch”这两个词无关紧要。这里有趣的一点是更多涉及和扩展的语法,有两个部分,每个部分都使用这种间接对象表示法。

有问题的代码实际上有method BLOCK LIST形式,但这也通过间接对象语法 into (do BLOCK)->method(LIST),其中do BLOCK需要为有意义的方法调用生成包名称或祝福(对象)引用。这在下面的Deparse输出中可以看到。

在此代码上使用B::Deparse编译器后端(通过O模块)

use strict; 
use warnings;
use feature 'say';

try   { call_a( 'x' ) } 
catch { 
    die "ACTUALLY die";
    #say "NO DONT die";
};

sub call_a { 
    die "Yes it dies";
    #say "no die";
}

perl -MO=Deparse script.pl应该显示运行的非常接近的近似值:

使用警告;
使用严格;
使用功能“说”;
尝试 {
    call_a('x')
} 做 {
    死'实际上死'
}->捕捉;
子调用_a {
    使用警告;
    使用严格;
    使用功能“说”;
    die '是的,它死了';
}
undef_sub.pl 语法OK

嵌套的间接对象语法显然太多了Deparse,仍然method BLOCK LIST在输出中留下形式。等效代码可以拼写为

(do { call_a('x') })->try( (do { die("ACTUALLY die") })->catch() );

在这种情况下更简单

call_a('x')->try( die("ACTUALLY die")->catch() );

因此,原始代码被解释为有效的语法 (!),它是( )之后的块的内容首先运行trycall_a('x')--- 所以程序死了,永远不会去“方法” try

如果我们将示例更改为

use strict;
use warnings;
use feature 'say';

try   { call_a( 'x' ) }
catch {
    #die "ACTUALLY die"; 
    say "NO DONT die";
};

sub call_a {
    #die "Yes it dies";
    say "no die";
}

无处die可去。运行它-MO=Deparse以查看

use warnings;
use strict;
use feature 'say';
try {
    call_a('x')
} (catch {
    say 'NO DONT die'
} );
sub call_a {
    use warnings;
    use strict;
    use feature 'say';
    say 'no die';
}
undef_sub.pl syntax OK

它现在采用直接method {} args语法(其args本身也Deparse以间接对象表示法显示)。等效代码是

call_a('x')->try( say("NO DONT die")->catch() );

首先是去哪里,在它返回之后,接下来运行方法调用call_a()中参数列表的代码。try我们没有遇到 adie并且实际运行如下

不死
不,不要死
不能在没有包或对象引用的情况下调用方法“catch”...

所以现在确实出现了“catch”方法的问题。

感谢ikegami的评论


如果上面的块返回一个确实有方法的包(或对象引用)的名称,catch那么try最终也会尝试

use strict; 
use warnings;
use feature 'say';

BEGIN {
    package Catch;
    sub catch { say "In ", (caller(0))[3] };
    $INC{"Catch.pm"} = 1;
};

use Catch;

try   { call_a( 'x' ) } 
catch { 
    say "NO DONT die";
    "Catch";
};

sub call_a { say "no die" }

现在我们有等价的

call_a('x')->try( do { say("NO DONT die"); 'Catch' }->catch() );

与输出

不死
不,不要死
在 Catch::catch 中
无法在 undef_sub.pl 第 14 行调用没有包或对象引用的方法“try”。

推荐阅读