Search code examples
perl

PerlCritic example: loop iterator is not lexical


When debugging some older perl code I use perl critic as a way to spot bugs. I stumble across this 'Loop iterator is not lexical' policy often: Perl::Critic::Policy::Variables::RequireLexicalLoopIterators And its never been a bug. When I implement the authors example in a test program, I dont seem to reproduce the problem it attempts to solve:

#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

my $bicycle = {is_red => 1, has_baseball_card_in_spokes => 1, has_bent_kickstand => 1, locked => 1};

my $bicycle1 = {is_green => 1, has_baseball_card_in_spokes => 1, has_bent_kickstand => 1, locked => 1};

my $bicycle2 = {is_blue => 1, has_baseball_card_in_spokes => 0, has_bent_kickstand => 0, locked => 1};

my @things_attached_to_the_bike_rack = ($bicycle,$bicycle1,$bicycle2);

for $bicycle (@things_attached_to_the_bike_rack) {
    if (
            $bicycle->{is_red}
        and $bicycle->{has_baseball_card_in_spokes}
        and $bicycle->{has_bent_kickstand}
    ) {
        $bicycle->{locked} = 0;

        last;
    }
}

if ( $bicycle and !$bicycle->{locked} ) {
    print "riding home in time for dinner\n";
}

Admittedly I did not make a blessed object with functions, but a hash reference instead, so perhaps it has something to do with objects in Perl. Even if I re-arrange the order of the array I do arrive home in time for dinner every time. So I must be missing something obvious, and I'd like a reproducible example to show the case when this is a problem.

I realize there are old questions about this, but I was wondering if this was still a relevant policy in late 2021.


Solution

  • Your code is different from the policy example. Your code assigns a value to $bicycle outside the loop, whereas the policy example does not.

    The following is code that more closely represents the intention of the policy example:

    use strict;
    use warnings;
    use diagnostics;
    
    my $bicycle0 = {is_red => 1, has_baseball_card_in_spokes => 1, has_bent_kickstand => 1, locked => 1};
    my $bicycle1 = {is_green => 1, has_baseball_card_in_spokes => 1, has_bent_kickstand => 1, locked => 1};
    my $bicycle2 = {is_blue => 1, has_baseball_card_in_spokes => 0, has_bent_kickstand => 0, locked => 1};
    
    my @things_attached_to_the_bike_rack = ($bicycle0, $bicycle1, $bicycle2);
    
    my $bicycle;
    for $bicycle (@things_attached_to_the_bike_rack) {
        if (
                $bicycle->{is_red}
            and $bicycle->{has_baseball_card_in_spokes}
            and $bicycle->{has_bent_kickstand}
        ) {
            $bicycle->{locked} = 0;
    
            last;
        }
    }
    
    if ( $bicycle and !$bicycle->{locked} ) {
        print "riding home in time for dinner\n";
    }
    

    When I run this, I do not see the riding home in time for dinner message, as expected, like I do when I run your original code.