In a comment on an answer to a question about hash slices, someone wanted to know how to use arrow syntax to access a hash slice through a hash reference, thinking perhaps that
@$ref->{@keys}
would do so.
Yes, the correct syntax is either @$ref{@keys}
or @{$ref}{@keys}
, but that's beside the point of this question.
I tried to work out the data structure that the expression with an arrow requires:
#! /usr/bin/env perl
use strict;
use warnings;
my $ref = {"a" => 1, "b" => 2, "c" => 3};
my @keys = qw/ a b c /;
#$ref = [ { a => 9, b => 8, c => 7 } ];
#$ref = [ { a => {}, b => {}, c => {} } ];
print @$ref->{@keys}, "\n";
As written, the code fails with
Not an ARRAY reference at ./prog line 12.
That makes sense: @$ref
wants a reference to an array, so I tried wrapping hash references inside a reference to an anonymous array. Those attempts failed with
Can't use an undefined value as a HASH reference at ./prog line 12.
The trace output is
$ debugperl -Dt prog [...] (prog:12) pushmark (prog:12) padsv($ref) (prog:12) rv2av (prog:12) rv2hv Can't use an undefined value as a HASH reference at prog line 12.
The syntax dump for the print
line is
$ debugperl -Dx prog [...] { 484 TYPE = print ===> 2 FLAGS = (VOID,KIDS) { 485 TYPE = pushmark ===> 486 FLAGS = (SCALAR) } { 372 TYPE = helem ===> 371 FLAGS = (SCALAR,KIDS) { 487 TYPE = rv2hv ===> 361 TARG = 5 FLAGS = (SCALAR,KIDS,REF) PRIVATE = (STRICT_REFS) { 373 TYPE = rv2av ===> 487 TARG = 4 FLAGS = (SCALAR,KIDS,REF) PRIVATE = (STRICT_REFS) { 486 TYPE = padsv ===> 373 TARG = 1 FLAGS = (SCALAR,MOD) } } } { 361 TYPE = padav ===> 372 TARG = 2 FLAGS = (SCALAR) } } { 371 TYPE = const ===> 484 TARG = 19 FLAGS = (SCALAR) } } [...]
Where is the undefined value coming from? For what values of $ref
does the program terminate normally?
It's not valid (or at least not meaningful) Perl syntax — I'm a bit surprised it's not flagged as a syntax error.
I first thought it was trying to evaluate the array @$foo
in scalar context and use the result as a hash reference, but that doesn't quite seem to be what's going on.
Rather, from the debug output you posted, it looks more like it's trying to directly use the internal array variable (AV) structure as a hash reference (RV), which is a type of scalar (SV; see perlguts for details).
I haven't looked at the source, but it looks as if the rv2hv
routine either notices that it's been given the wrong kind of structure and returns null, or just tries to use the AV as an RV and achieves the same effect that way. (Sorry if that may sound a bit muddled, it's been some years since I last looked at the internals of perl.)
You might want to consider submitting a bug report.
BTW, a simpler test case that demonstrates the effect is just @foo->{bar}
.
@$ref->{@keys}
means
scalar(@$ref)->{@keys}
so it should be equivalent to
my $ref2 = @$ref;
$ref2->{@keys}
It's not, so it's a bug. It's still present in the near-current state of what will become Perl 5.16.0. (v5.15.4, to be specific)
Please report using the perlbug
command line tool. (Just enter perlbug
and answer a few simple questions.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With