How to pass hashes to subroutines in Perl?
Question
How to pass hashes to subroutines in Perl?
Passing hashes to subroutines in Perl can be a little tricky for beginners due to the way Perl handles function arguments and context. Unlike scalars, you cannot directly pass a hash as a single argument without using a reference, because when you pass a hash to a subroutine, it gets flattened into a list of key-value pairs. This behavior often causes unexpected results if you try to manipulate the hash inside the subroutine without proper referencing.
How Subroutine Arguments Work in Perl
Perl subroutines receive their arguments as a flat list in the special array @_. This means if you pass multiple scalars or entire hashes, they all get combined into one big list. The sigil ($, @, or %) you use in the subroutine influences how you interpret those arguments.
Passing a Hash by Value (Flattened)
If you pass a hash directly like this:
sub show_hash {
my %hash = @_; # @_ is a flat list, assign to %hash
while (my ($k, $v) = each %hash) {
print "$k => $v\n";
}
}
my %data = (foo => 1, bar => 2);
show_hash(%data);
This works fine for a single hash. But it only works reliably when you pass exactly one hash because the hash is flattened into key-value pairs. If you have multiple hashes or additional parameters, key-value pairs can mix and cause errors.
Passing Hashes Using References (Safer and Recommended)
A more robust and idiomatic way is passing a hash reference to the subroutine. This passes a scalar containing a pointer to the hash rather than flattening. Inside the subroutine, you dereference it back to a hash.
sub show_hash_ref {
my ($hash_ref) = @_; # Get the first argument - a hash reference
my %hash = %{$hash_ref}; # Dereference it to a hash
while (my ($k, $v) = each %hash) {
print "$k => $v\n";
}
}
my %data = (foo => 1, bar => 2);
show_hash_ref(\%data); # Pass a reference to the hash
This technique allows passing multiple hashes or additional scalar arguments without confusion, because references are scalars and do not flatten.
Summary of Approaches
- Pass hash directly: easy for single hash, but flattens and risks key-value mixing
- Pass hash reference: recommended, avoids flattening, safer with multiple arguments
- Dereference inside subroutine: using
%{$hash_ref}converts the reference back to a hash
Complete Example Putting It All Together
#!/usr/bin/perl
use strict;
use warnings;
# Subroutine receiving a hash directly (by value)
sub show_hash {
my %hash = @_; # @_ is a flat list of key-value pairs
print "Inside show_hash (by value):\n";
while (my ($key, $value) = each %hash) {
print " $key => $value\n";
}
}
# Subroutine receiving a hash reference
sub show_hash_ref {
my ($href) = @_; # First argument: hash reference
print "Inside show_hash_ref (by reference):\n";
while (my ($key, $value) = each %{$href}) {
print " $key => $value\n";
}
}
my %hash = (apple => 'red', banana => 'yellow', grape => 'purple');
# Pass hash directly (flattened)
show_hash(%hash);
# Pass hash reference
show_hash_ref(\%hash);
This script will print the contents of the hash twice, once using direct passing and once using a reference. Because the direct passing flattens the hash, do not use that method if you have multiple hashes or extra parameters; otherwise, you might get unexpected argument mixing.
Common Pitfalls to Avoid
- Passing multiple hashes directly: hashes flatten and merge their key-value pairs.
- Assuming
@_contains individual hashes when they are flattened lists. - Not using
strictandwarningscan mask subtle bugs. - Dereferencing incorrectly (e.g., using
@{$href}instead of%{$href}for hashes).
Using hash references leads to more readable, maintainable, and bug-resistant code — especially as your subroutines get more complex.
Verified Code
Executed in a sandbox to capture real output. • v5.34.1 • 5ms
bar => 2
foo => 1
(empty)Was this helpful?
Related Questions
- How to use the return statement early in a Perl subroutine?
- How to create higher-order functions in Perl?
- How to use shift to get subroutine arguments in Perl?
- How to use caller() to get subroutine call information in Perl?
- How to call a subroutine as a method in Perl?
- How to use state variables in Perl subroutines?