Find the bug!

Ovid on 2006-03-03T23:06:40

I had to add a new feature to my Array::AsHash module. Basically, you might need to get keys and values at particular array indices (think "a list of pairs"), so I needed to allow this:

my $key  = $array->key_at(2);
my @keys = $array->key_at(0, 3, -1);
my $keys = $array->key_at(0, 3, -1); # aref

So I wrote code like this (I did something similar for values):

sub key_at {
    my $self  = CORE::shift;
    my $ident = ident $self;
    my @keys;
    foreach my $index (@_) {
        $index *= 2;
        CORE::push @keys => $array_for{$ident}[$index];
    }
    return wantarray ? @keys
        : 1 == @_    ? $keys[0]
        :              \@keys;
}

See the bug? There are a couple of subtle issues here, but one of them is a real whopper. When you see it, you'll know.

FYI: The CORE:: stuff is there because I have methods named shift and push. ident is from Class::Std.


Hmmm...

rjray on 2006-03-04T00:06:19

Well, this is a stab in the dark, but when I see -1 being used in a fetch-from-array context, I tend to assume it's counting from the end of the array, not being treated directly as an index. Other than that, you aren't checking whether you're called on a reference or statically, which leads me to think that "ident $self" might not always return what you expect it to return.

Re:Hmmm...

Ovid on 2006-03-04T01:43:12

Trust me. Once you see the bug, it's a forehead slapper -- if you've encountered this behavior before. It's something that folks often miss because it occurs so rarely, but it's a natural consequence of how Perl behaves.

D'oh!

rjray on 2006-03-10T20:14:36

*sound of forehead-slapping*

Re:D'oh!

Ovid on 2006-03-10T20:19:34

Yeah, I know. It bit me, too :)

Yeah

joel h on 2006-03-04T00:14:33

(Rot-13'ed)
Lbh'er zbqvslvat gur inyhrf bs gur cnenzrgre yvfg juvpu ner nyvnfrq gb gur inyhrf bs gur inevnoyrf va gur pnyyre'f fpbcr, gurersber zbqvslvat gur pnyyre'f fpbcr, be va guvf pnfr, "ernq-bayl inyhrf".

Of course, your tests should have found this instantly, if you used something like the examples you have there, as it throws up an immediate error.

Why does Perl do this? It's bitten me in the ass a few times, and I can't seriously imagine using it deliberately in code to be maintained.

Re:Yeah

Ovid on 2006-03-04T01:44:12

Right in one! Fortunately, the fix is trivial, but it's strange to see if folks don't know what's going on, so I document it.

Re:Yeah

Smylers on 2006-03-04T07:51:54

Why does Perl do this? It's bitten me in the ass a few times, and I can't seriously imagine using it deliberately in code to be maintained.

I believe Larry also considers this to be a mistaken ‘feature’: in Perl 6 sbe nyvnfrf ner ernq-bayl ol qrsnhyg, which would've avoided this bug.

(The bit in italics is a spoiler for the original question, so I've put it in Welsh ... erm, I mean rot-13.)

Smylers