FATAL 未初始化警告 - 远距离操作

FATAL uninitialized warnings - action at a distance

我最近遇到了一个错误,当时 use warnings FATAL ... pragma 将来自其他地方的静音警告解释为死亡的原因。考虑以下示例:

use strict;
# In one file: 

no warnings;
my %hash;
Foo->bar( my $temp = $hash{ +undef } ); # this lives
Foo->bar( $hash{ +undef } ); # this dies

# Elsewhere

package Foo;
use warnings FATAL => qw(uninitialized);

sub bar {
    my ($self, $param) = @_; # prefectly safe
    $param = "(undef)"
        unless defined $param; # even safer
    print "Param: $param\n"; 
}

现在这当然可以在整个项目中使用关于警告的相同策略来解决。或者可以通过在特定位置排除 undefs 来解决每次发生的问题(参见 # this lives 行)。

我的问题是是否有一个不需要更改任何调用它的包 Foo 的可接受解决方案,以及这是否真的是 Perl 本身的错误。

这不是错误。您正在体验 side-effect 一项功能,该功能可防止对传递给 subs 的散列元素进行不必要的自动验证。


Perl 通过引用传递。这意味着在函数内更改参数将更改外部参数。

$ perl -E'
   sub f { $_[0] = "xyz"; }
   f($x);
   say $x;
'
xyz

这也适用于散列元素。

$ perl -E'
   sub f { $_[0] = "xyz"; }
   my %h;
   f($h{x});
   say $h{x};
'
xyz

sub 对散列一无所知,因此必须在输入 sub 之前创建散列元素,以便分配一些内容。

...或者是吗?如果元素不存在,通常不希望 f($h{x}) 总是创建 $h{x}。因此,Perl 推迟执行散列查找直到 $_[0] 被访问,此时它知道该元素是否需要被激活。这就是警告来自子程序的原因。

具体来说,当您调用 f($h{x}) 时,Perl 不会将 $h{x} 传递给子程序。相反,它传递了一个神奇的标量,其中包含对 %h 的引用和键值 (x)。这会推迟执行哈希查找,直到 $_[0] 被访问,在那里知道 $_[0] 是否在可分配的地方使用。

  • 如果 $_[0] 以不改变的方式使用(即,如果它用作右值),则查找散列元素而不激活它。

  • 如果 $_[0] 以可以改变的方式使用(即,如果它用作左值),哈希元素将被激活并返回。

$ perl -E'
   sub f { my $x = $_[0]; }  # $_[0] returns undef without vivifying $h{x}
   sub g { $_[0] = "xyz"; }  # $_[0] vivifies and returns $h{x}
   my %h;
   f($h{x});
   say 0+keys(%h);
   g($h{x});
   say 0+keys(%h);
'
0
1