Home / Alt manpages / perlref(1)

  • perlref(1)
  • User command
  • linux

Build and Safely Traverse Nested Perl Data with References

You will finish with a small Perl program that builds a nested data structure, updates it through references, and reads it back without guessing which sigil belongs where. The examples use Perl 5.38.2 and the matching perl-doc package installed on this machine.

Allow about fifteen minutes. You need Perl and a text editor. Everything here runs as an ordinary user and changes no files unless you save the example yourself.

1. Check the Perl version

Start by recording the interpreter that will run the examples:

$ perl --version
This is perl 5, version 38, subversion 2 (v5.38.2)
$ dpkg-query -W -f='${Package} ${Version}\n' perl-doc
perl-doc 5.38.2-3.2ubuntu0.6

The perlref manual is the complete reference for hard references, dereferencing, nested data, closures and circular references. This guide uses the common operational subset. Put use strict; and use warnings; in short programs so misspelled variables and suspicious behaviour are reported early.

2. Build a nested record

Use square brackets for an anonymous array and curly brackets for an anonymous hash. The variable holds a scalar reference, so the outer value is one thing that can be passed around:

use strict;
use warnings;

my $record = {
    name   => 'Ada',
    scores => [91, 88],
};

print $record->{name}, "\n";
print $record->{scores}[0], "\n";

Run it with a temporary file or paste it into a Perl REPL. The expected output is:

Ada
91

The arrow is the readable form for looking through a reference. $record->{name} means the name value in the hash referenced by $record. The arrow between {scores} and [0] is optional, so $record->{scores}[0] is valid and avoids visual clutter.

3. Update the referenced data

Dereferencing is not a copy. Mutating a value through the reference changes the referenced hash or array:

push @{ $record->{scores} }, 95;
printf "%s: %d\n", $record->{name}, $record->{scores}[2];

Expected output:

Ada: 95

The @{ ... } form makes it explicit that the expression inside produces an array reference. For one element, arrow notation is usually clearer. Do not confuse $record->{scores}[0] with $record->{scores}[0] typed against some other variable: Perl applies the dereference to the scalar that immediately supplies it, then performs the lookup.

Checkpoint: if the value is not what you expect, print its type before changing it:

print ref($record), "\n";
print ref($record->{scores}), "\n";

The expected types are HASH and ARRAY. An undefined value or a different type usually means the structure was built with the wrong brackets or key.

4. Use a reference as a function argument

References are useful when a subroutine needs to inspect or update a whole structure. Pass the scalar reference, then dereference it inside the subroutine:

sub add_score {
    my ($record, $score) = @_;
    push @{ $record->{scores} }, $score;
}

add_score($record, 97);
print $record->{scores}[3], "\n";

This prints 97. The subroutine receives a reference to the existing hash, not a separate nested copy. That is intentional here. If a helper should not mutate its input, have it return a new structure or make a deliberate copy. Do not assume that assigning a reference creates independent data.

5. Let Perl create missing branches deliberately

Perl can create intermediate references when you assign through an undefined value in a nested lvalue. This is called autovivification:

my @rows;
$rows[0]{meta}{name} = 'Ada';
print $rows[0]{meta}{name}, "\n";

The result is:

Ada

Perl has created an array element, then a hash reference for meta, then its name value. This is convenient for known paths, but it can hide a typo: writing $rows[0]{mtea}{name} creates a second branch instead of reporting that meta was misspelt.

Checkpoint: inspect the structure after a construction step rather than trusting a final value. For a quick, dependency-free check, print the keys you expect:

die 'missing meta branch' unless ref($rows[0]{meta}) eq 'HASH';
die 'missing name' unless exists $rows[0]{meta}{name};

When a path comes from input, validate each level before writing it. If you need stricter control over automatic creation, study the relevant no autovivification options and test them against the Perl version you deploy.

6. Keep state in a closure when a callback needs it

An anonymous subroutine can retain a lexical variable after the creating subroutine has returned. This is a closure, and it is useful for small callbacks with private state:

sub make_prefix {
    my ($prefix) = @_;
    return sub { "$prefix$_[0]" };
}

my $label = make_prefix('ref:');
print $label->('ok'), "\n";

Expected output:

ref:ok

The anonymous subroutine keeps the lexical $prefix alive. The arrow call, $label->('ok'), invokes the code reference. This is different from a string containing Perl code: no eval is needed, and there is no reason to turn untrusted text into executable code.

7. Avoid the common reference traps

  • Use [ ... ] when you need one array reference and ( ... ) when you need a list. A backslash applied to a list can produce a list of references, not one reference to the list.
  • Use -> to show that you are traversing a reference. $hashref->{key} is not the same as $hashref{key}; the latter refers to a package hash named %hashref.
  • Do not use symbolic references to turn input strings into variable names. Hard references are the normal choice and avoid treating data as names in the symbol table.
  • Keep circular structures in mind. A reference cycle can keep reference counts above zero, so automatic cleanup may not reclaim it. Break the cycle explicitly when the structure is no longer needed.

If a nested lookup fails, reduce it to one step at a time and print ref() at each branch. That usually exposes whether the problem is an undefined value, a hash where an array was expected, or a key spelling error.

Done means

  • You can create anonymous hashes and arrays with {...} and [...].
  • You can read and update nested values with arrow notation.
  • You can pass one structure to a subroutine without accidentally copying it.
  • You recognise autovivification and check branches where a typo would matter.
  • You can identify a closure and know why a reference cycle may need manual breaking.