views:

136

answers:

4

With warnings enabled, perl usually prints Use of uninitialized value $foo if $foo is used in an expression and hasn't been assigned a value, but in some cases it's OK, and the variable is treated as false, 0, or '' without a warning.

What are the cases where an uninitialized/undefined variable can be used without a warning?

+17  A: 

Summary

  • Boolean tests
  • Incrementing or decrementing an undefined value
  • Appending to an undefined value
  • Autovivification
  • Other mutators

Boolean tests

According to the perlsyn documentation,

The number 0, the strings '0' and '', the empty list (), and undef are all false in a boolean context. All other values are true.

Because the undefined value is false, the following program

#! /usr/bin/perl

use warnings;

my $var;
print "A\n" if $var;
$var && print "B\n";
$var and print "C\n";
print "D\n" if !$var;
print "E\n" if not $var;
$var or print "F\n";
$var || print "G\n";

outputs D through G with no warnings.

Incrementing or decrementing an undefined value

There's no need to explicitly initialize a scalar to zero if your code will increment or decrement it at least once:

#! /usr/bin/perl

use warnings;

my $i;
++$i while "aaba" =~ /a/g;
print $i, "\n";

The code above outputs 3 with no warnings.

Appending to an undefined value

Similar to the implicit zero, there's no need to explicitly initialize scalars to the empty string if you'll append to it at least once:

#! /usr/bin/perl

use warnings;
use strict;

my $str;
for (<*>) {
  $str .= substr $_, 0, 1;
}
print $str, "\n";

Autovivification

One example is "autovivification." From the Wikipedia article:

Autovivification is a distinguishing feature of the Perl programming language involving the dynamic creation of data structures. Autovivification is the automatic creation of a variable reference when an undefined value is dereferenced. In other words, Perl autovivification allows a programmer to refer to a structured variable, and arbitrary sub-elements of that structured variable, without expressly declaring the existence of the variable and its complete structure beforehand.

For example:

#! /usr/bin/perl

use warnings;

my %foo;
++$foo{bar}{baz}{quux};

use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%foo;

Even though we don't explicitly initialize the intermediate keys, Perl takes care of the scaffolding:

$VAR1 = {
  'bar' => {
    'baz' => {
      'quux' => '1'
    }
  }
};

Without autovivification, the code would require more boilerplate:

my %foo;
$foo{bar} = {};
$foo{bar}{baz} = {};
++$foo{bar}{baz}{quux};  # finally!

Don't confuse autovivification with the undefined values it can produce. For example with

#! /usr/bin/perl

use warnings;

my %foo;
print $foo{bar}{baz}{quux}, "\n";
use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%foo;

we get

Use of uninitialized value in print at ./prog.pl line 6.

$VAR1 = {
  'bar' => {
    'baz' => {}
  }
};

Notice that the intermediate keys autovivified.

Other examples of autovivification:

  • reference to array

    my $a;
    push @$a => "foo";
    
  • reference to scalar

    my $s;
    ++$$s;
    
  • reference to hash

    my $h;
    $h->{foo} = "bar";
    

Sadly, Perl does not (yet!) autovivify the following:

my $code;
$code->("Do what I need please!");

Other mutators

In an answer to a similar question, ysth reports

Certain operators deliberately omit the "uninitialized" warning for your convenience because they are commonly used in situations where a 0 or "" default value for the left or only operand makes sense.

These are: ++ and -- (either pre- or post-), +=, -=, .=, |=, ^=, &&=, ||=.

Being "defined-or," //= happily mutates an undefined value without warning.

Greg Bacon
Are there cases where that doesn't work? Is it just in lvalue contexts or does it work as an rvalue too? I could swear I've seen warnings in cases like this, but I'm not sure of the details.
Frank Szczerba
@Frank With `my %h; print %h{a}{b}`, you'll get both a warning and autovivification of the intermediate key. See edited answer.
Greg Bacon
@gbacon: ok, that's what I thought (though I think you mean `$h{a}{b}`, not `%h{a}{b}`).Nice answer.
Frank Szczerba
@Greg This could be counted as a boolean test but even so, I think it could deserve explicit mentioning: `my $x; $x = !$x;` assigns 1 (true) to $x without warning.
Peter G.
+3  A: 

So far the cases I've found are:

  • autovivification (gbacon's answer)
  • boolean context, like if $foo or $foo || $bar
  • with ++ or --
  • left side of +=, -=, or .=

Are there others?

Frank Szczerba
A: 

The real answer should be: why would you want to turn on that warning? undef is a perfectly good value for a variable (as anyone who's ever worked with a database can tell you), and it often makes sense to differentiate between true (something happened), false (nothing happened) and undef (an error occurred).

Rather than saying

use strict;
use warnings;

say

use common::sense;

and you'll get all the benefits of warnings, but with the annoying ones like undefined variables turned off.

common::sense is available from the CPAN.

Sam Kington
common::sense is the scourge of CPAN
mikegrb
I'm not sure about `The Scourge` we still have 100 worthless class creation frameworks that are at least as bad.
Evan Carroll
See http://stackoverflow.com/questions/1625839/should-i-use-commonsense-or-just-stick-with-use-strict-and-use-warnings for discussion of common::sense.
daotoad
The whole point of that warning is to alert you when you are using an undef variable in a way that suggests you expect it to be defined. In the cases where undef makes sense as a distinct value, treat it like a distinct value, and you won't get warnings.
Frank Szczerba
@Frank That would make sense if use warnings was able to read your mind. In my experience, though, the undefined variable warning is mostly annoying and can only be avoided by writing what, to my mind, is fussy and unPerlish code, or by turning it off entirely.
Sam Kington
+2  A: 

Always fix warnings even the pesky annoying ones.

Undefined warnings can to be turned off. You can do that by creating a new scope for the operation. See perldoc perllexwarn for more info. This method works across all versions of perl.

{
  no warnings 'uninitialized';
  my $foo = "foo" + undef = "bar";
}

For a lot of the binary operators, you can use the new Perl 5.10 stuff, ~~ and //; See perldoc perlop for more info.

use warnings;
my $foo = undef;
my $bar = $foo // ''; ## same as $bar = defined $foo ? $foo : ''

also is the //= variant which sets the variable if it is undefined:

$foo //= '';

The Smart Matching (~~) operator is kind of cool, and permits smart comparisons, this is kind of nifty check it out in perldoc perlsyn:

use warnings;
my $foo = "string";
say $foo eq undef;  # triggers warnings
say $foo ~~ undef;  # no undef warnings
Evan Carroll

related questions