Locked hashes

[ Perl tips index ]
[ Subscribe to Perl tips ]

Perl's most popular pragma is 'use strict', and most experienced programmers start their code by making use of strict mode. The reason for this is that using strict allows Perl to catch common programming errors -- in particular, typographic errors when working with variables. In the following example, strict catches the error where @friends is spelled incorrectly as @freinds:

        use strict;
        my @friends = qw(Wilma Barney Betty);
        print "My friends are @freinds\n";

However, using strict doesn't afford us the same consideration when it comes to hashes. Hashes are regularly used for data sets, and as the basis of objects. However a typo when assigning to a hash key does not produce an error -- Perl simply creates the new key/value pair. This process is known technically as 'autovivication'.

        my %employee = (
                name     => "Fred Flintstone",
                address  => "302 Cobblestone Way",
                wage     => 20000,

        # Fred may deserve an increase in pay, but due to a typo we instead
        # create a new (and unwanted) key in our hash.

        $employee{waeg} = 30000;

Autovivifying hash keys can be very handy, but sometimes it would be nice to be able to create a hash where our keys are fixed, so we can't create new keys by accident. This would avoid the bug in our code immediately above. The good news is that in recent versions of Perl (>= 5.8.0) we can.

Perl's "Hash::Util" module allows us to specify a list of keys that a hash may contain, by using the 'lock_keys' function:

        use Hash::Util qw(lock_keys);

        # Do not allow any new keys to be added to our hash.  Existing
        # keys will remain, and their values can still be modified.


        # Create a new hash, and specify the list of keys that we wish
        # to permit it to possess, even though we are not setting those
        # keys at this time.

        my %aircraft;
        lock_keys(%aircraft,qw(model make engine seats owner));

If you specify a specific list of allowed keys, and the current hash contains keys that are not in this list, then Perl will throw an exception.

It is not possible to 'bless' a hash while it is in a locked state. However you can bless your hash before locking it, or unlock it, bless it and relock. To unlock a hash, we use Hash::Util's unlock_keys function:

        use Hash::Util qw(unlock_keys);

However, Hash::Util's usefulness does not stop there. It's also possible for us to lock the values inside a hash, either individually or all the keys at once.

This allows us to define a hash where some parts are invariant (such as a car's make and model) but other parts can be varied (such as the owner, kilometres, and amount of fuel in the tank). The hash's keys must already be locked already, otherwise this results in an error:

        use Hash::Util qw(lock_keys lock_value lock_hash);

        # The hash keys must be locked before locking values.

        # We do not allow employees to change their name.

        # Lock all the keys and the values in our hash, making it
        # completely read-only.  No keys or values can be changed,
        # added or deleted.


In the same way that it is possible to unlock keys when we need to, it's also possible to unlock values. This allows us to knowingly change hash values, but prevent the case where they might be changed by accident.

	use Hash::Util qw(unlock_value lock_value);

	# Our new policy requires employees have a middle initial
	# in their name as well.  We can unlock our value to allow
	# this unusual change.

	$employee{name} = "Fred J. Flintstone";

Finally, we can unlock an entire hash, allowing all keys and all values to be changed:

        use Hash::Util qw(unlock_hash);


You may find Hash::Util useful if you are:

The ability to lock parts or all of a hash in Perl is a powerful way to prevent typographic errors, and to protect important data from accidental change. The Hash::Util module is a standard module with Perl in versions 5.8.0 and above.

[ Perl tips index ]
[ Subscribe to Perl tips ]

This Perl tip and associated text is copyright Perl Training Australia. You may freely distribute this text so long as it is distributed in full with this Copyright noticed attached.

If you have any questions please don't hesitate to contact us:

Email: contact@perltraining.com.au
Phone: 03 9354 6001 (Australia)
International: +61 3 9354 6001

Valid XHTML 1.0 Valid CSS