未定义的只读变量在布尔上下文中计算为true

时间:2013-08-02 13:16:57

标签: perl

在我们的代码库中,我们经常使用此构造来处理程序的选项:

Readonly my $ARGS => Getopt::Declare->new(
   join( "\n",
      '[strict]',
      "--option1 <some-option-description> [required]",
   )
) or exit(1);

问题是,当Getopt::Declare->new(...)中违反严格时,它将返回undef,这通常会正常,但此undef存储在 Readonly 变量,在布尔上下文中计算时为 true

  1. 一个明显的解决方案是删除Readonly,代码将按预期运行;但我真的不喜欢这个解决方案,因为改变它会允许修改$ARGS

    my $ARGS => Getopt::Declare->new(
       join( "\n",
          '[strict]',
          "--option1 <some-option-description> [required]",
       )
    ) or exit(1);
    
  2. 另一个解决方案是将Getopt::Declare->new(...) or exit(1)包装在parens中,以便在分配给 Readonly 变量之前对它们进行评估。

    Readonly my $ARGS => (
       Getopt::Declare->new(
          join( "\n",
             '[strict]',
             "--option1 <some-option-description> [required]",
          )
       ) or exit(1);
    )
    
  3. 我想另一种方法是使用更高优先级的版本或||,它比=>强,但我不确定它是否可读。

    Readonly my $ARGS => Getopt::Declare->new(
       join( "\n",
          '[strict]',
          "--option1 <some-option-description> [required]",
       )
    ) || exit(1);
    

2 个答案:

答案 0 :(得分:3)

Readonly my $ARGS => Getopt::Declare->new(
# ...
);
$ARGS or exit(1);

应该也可以。

答案 1 :(得分:1)

@choroba建议的直接问题是优先考虑的问题之一:

Readonly my $v => ...      or die;     # This construct...
Readonly(my $v, ...)       or die;     # is really this...
tie my $v, 'Readonly', ... or die;     # which is more-or-less this, which evaluates to ...  
ReadOnly::Scalar=SCALAR(0x123) or die; # the object beneath the tie() ...

因此,您永远不会点击or的右半部分,因为返回的对象的计算结果为真。

现在,如果你真的想继续使用这种语法,你可以做类似的事情来强制使用tie()d变量而不是底层对象:

use Readonly ();
sub MakeReadonly(\[$@%]@) {
  &Readonly::Readonly;               # tie() ${$_[0]} to Readonly implementation
  ${$_[0]};                          # ... but return the tied variable, not the object
}

....

MakeReadonly my $e => ... or die;    # This ...
MakeReadonly(my $e, ...)  or die;    # becomes this ...
$e or die;                           # which is effectively this.

顺便说一句,我建议小心这个结构。 Readonly的吸引力在于它允许语法看似类似于分配给属性标量,就好像你这样做:

my $v : Readonly = something() or die;    # XXX Doesn't work!  Not even Attribute::Constant
                                          # works like this, unfortunately.

......但它完全不同。你在帖子的标题和正文中的短语表明你至少最初是这样想的。 (例如,“Readonly变量评估为true” - 好吧,它们的底层对象确实如此,绑定变量本身可能不是。或者,“在分配之前进行评估” - 好吧,你实际上并没有真正分配本身,但又是tie()ing。)