subroutines intermediate

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 strict and warnings can 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

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
bar => 2
foo => 1
STDERR
(empty)

Was this helpful?

Related Questions