Home / Alt manpages / perldsc(1)

  • perldsc(1)
  • User command
  • linux

Build and Inspect Nested Perl Data Structures Safely

You will finish with a small Perl program that builds an array of records, adds nested data, prints selected fields, and exposes the reference mistake that most often corrupts a nested structure. The examples follow perldsc(1) as installed with Perl 5.38.2.

Allow about 20 minutes. You need Perl and a text editor. The commands run as your normal user, create only a temporary source file, and do not need elevated privileges.

1. Confirm the Perl version and documentation

Check the interpreter before relying on newer reference syntax:

$ perl -v | sed -n '1,2p'
This is perl 5, version 38, subversion 2 (v5.38.2)

$ man perldsc

The installed document describes postfix dereferencing such as $rows->@*. It says that this form was added in 2014 and is clearer to read, while older code may use circumfix dereferencing such as @{$rows}. If your interpreter is older, use the older form or check its supported feature set.

Checkpoint: record the version printed by your own machine. Examples below are written for the installed 5.38.2 interpreter, not for an unspecified Perl release.

2. Start with references, not imaginary multidimensional arrays

Perl arrays and hashes are one-dimensional containers of scalar values. A scalar can hold a reference to another array or hash, which is what makes nested access possible. These four expressions describe the common shapes:

$array[7][12]                 # array of arrays
$array[7]{name}              # array of hashes
$hash{group}[7]              # hash of arrays
$hash{group}{name}           # hash of hashes

Perl does not implicitly dereference a reference when you print the outer container. Print an individual value, or explicitly walk the referenced value:

my @rows = ( [ 'red', 3 ], [ 'blue', 5, 7 ] );

print $rows[1][2], "\n";     # prints 7
print @rows, "\n";           # prints reference descriptions

for my $row ( @rows ) {
    print '[ ', join(', ', $row->@*), " ]\n";
}

Expected output is:

7
ARRAY(...)ARRAY(...)
[ red, 3 ]
[ blue, 5, 7 ]

The hexadecimal parts of the reference descriptions vary. The useful output is the final two lines.

3. Create each nested value with a constructor

Use square brackets for a new array reference and braces for a new hash reference. This is the safe default when populating a structure in a loop:

use v5.36;

my @people;
for my $name ( 'Ada', 'Grace', 'Ken' ) {
    push @people, {
        name   => $name,
        skills => [ 'Perl', 'Linux' ],
    };
}

for my $person ( @people ) {
    printf "%s: %s\n", $person->{name}, join(', ', $person->{skills}->@*);
}

use v5.36 enables strictness, warnings and the useful feature bundle described by the manpage. It also makes undeclared variables and many accidental mistakes fail early. Expected output is:

Ada: Perl, Linux
Grace: Perl, Linux
Ken: Perl, Linux

Checkpoint: save this as /tmp/perl-structures.pl and run perl /tmp/perl-structures.pl. A clean run with the three lines above means the basic nesting and dereferencing syntax works.

4. Keep copies separate from aliases

The most damaging mistake is storing the same array reference repeatedly. This broken pattern leaves every row pointing at the same array:

my @rows;
my @row;
for my $number ( 1 .. 3 ) {
    @row = ( $number, $number * 10 );
    push @rows, \@row;          # WRONG: the same reference each time
}

for my $row ( @rows ) {
    print join(',', $row->@*), "\n";
}

All three lines contain the final values, because \@row refers to the same variable on every iteration. Do not repair this by guessing about scope. Make a fresh copy with an array constructor:

my @rows;
my @row;
for my $number ( 1 .. 3 ) {
    @row = ( $number, $number * 10 );
    push @rows, [ @row ];        # a new array reference and a copy
}

for my $row ( @rows ) {
    print join(',', $row->@*), "\n";
}

Expected output is:

1,10
2,20
3,30

There is a subtle exception: a lexical declared with my inside the loop is recreated each iteration, so a reference to it can work. The manpage advises against teaching that trick as the normal pattern because it hides the lifetime distinction. Prefer [ @row ] when your intent is a separate copy.

5. Choose the shape that matches the lookup

An array of hashes suits ordered records:

my @servers = (
    { name => 'web-01', address => '192.0.2.10' },
    { name => 'db-01',  address => '192.0.2.20' },
);
print $servers[0]{address}, "\n";

A hash of arrays suits named groups whose members may grow:

my %teams = (
    platform => [ 'Ada', 'Grace' ],
    data     => [ 'Ken' ],
);
push $teams{platform}->@*, 'Linus';
print join(', ', sort $teams{platform}->@*), "\n";

A hash of hashes suits records addressed by a key:

my %inventory = (
    keyboard => { quantity => 4, location => 'rack-a' },
    monitor  => { quantity => 2, location => 'rack-b' },
);
print $inventory{monitor}{quantity}, "\n";

Use keys with sort when output order matters. Hash iteration order is not a presentation order you should depend on.

6. Inspect a structure before changing it

For a quick interactive inspection, run Perl's debugger and use its x command:

$ perl -d /tmp/perl-structures.pl
DB<1> x \@people
DB<2> q

The debugger prints nested arrays and hashes with indentation and reference types. Addresses and the exact prompt are variable; the point is to see whether each row has its own referenced container. Type q to leave the debugger. This only runs the program and inspects memory; it does not write a data file.

For a compile-only checkpoint, use:

$ perl -c /tmp/perl-structures.pl
/tmp/perl-structures.pl syntax OK

If strictness reports an undeclared variable, fix the spelling or add the intended lexical declaration. Do not remove use v5.36 merely to silence the diagnostic.

Done means

  • You can explain that nested arrays and hashes are built from references.
  • Your program uses use v5.36 and passes perl -c.
  • New nested rows use [ ... ] or { ... } where a separate value is intended.
  • You can distinguish a copied array from several references to one mutable array.
  • You have inspected the structure and know which lookup shape fits your data.