PBP: 071 Non-Lexical Loop Iterators

The statement made by this Best Practice is simple, “Always declare a for loop iterator with my.”  The reasoning behind it is complex, and surprised me.  I am sure I have screwed this up in the past, too.  It’s important as there’s a real gotcha here!

The gotcha isn’t that you should use an iterator variable.  The gotcha is that for always creates a new variable and then it always goes out of scope at the end of the block.  You can’t use an existing variable as the iterator, and you can’t access the contents after the loop.

Here’s what I mean:


#!/usr/bin/perl

use strict;

use warnings;

my $found = 'none';

my @search = qw/ a b c d e f g h /;

for $found (@search) {

        print "Checking $found...\n";

        last if $found eq 'f';

}

print "I found $found!\n";

What do you get when you run that?  I get:

$ perl forsample.pl
Checking a...
Checking b...
Checking c...
Checking d...
Checking e...
Checking f...
I found none!
$

The variable “found” was replaced by a new variable in the for loop, silently.  No warnings, no errors.

If you make the for loop “for my $found (@search) {” the behavior is the same.  There’s not even a warning about replacing the lexical with another one.

This surprised me!  I expected it to use the existing variable, and to warn if it was overlapping another variable.

Lesson is: For loop variables should be unique, so the code reflects this big hole in Perl.

One Response to “PBP: 071 Non-Lexical Loop Iterators”

  1. Will C says:

    I’ve taken to using List::MoreUtils qw/first_value/ for the “found” scenario you used as your example. It replaced a hand-rolled version :)

Leave a Reply