Hashes

exists#

Test whether a hash or array element, or a named subroutine, is present — without creating it and without caring what it holds.

exists asks a structural question: is there an entry at this location? It does not ask what the entry’s value is. An entry whose value is undef still exists. An entry that has never been assigned does not — looking it up with a plain read would return undef, but exists distinguishes the two cases.

Synopsis#

exists $hash{KEY}
exists $array[INDEX]
exists &subroutine
exists $ref->{A}{B}          # autovivifies $ref->{A}!

What you get back#

A boolean: 1 (a true scalar) if the element or subroutine is present, the empty string "" (a false scalar) if not. The result is always a plain scalar — there is no list-context variant.

my %h = (a => undef);
exists $h{a};                # 1   (key exists)
defined $h{a};               # ""  (value is undef)
exists $h{b};                # ""  (key does not exist)

exists is not defined#

The two answer different questions and they disagree exactly when an existing entry holds undef:

State

exists

defined

Plain read

Never assigned

false

false

undef

Assigned undef

true

false

undef

Assigned any other value

true

true

that value

Use exists when you need to know whether a key has been placed in the structure — e.g. an option hash where a bare key means “the caller opted in, value doesn’t matter.” Use defined when you need to know whether a value is usable.

Examples#

Hash-key test. Classic option-hash idiom — presence, not truth:

sub configure {
    my %opt = @_;
    my $verbose = exists $opt{verbose};     # caller passed verbose => anything
    my $color   = $opt{color} // "auto";    # caller-supplied or default
    ...
}

Distinguishing “missing” from “present but undef”:

my %cache = (user_42 => undef);             # cached: "we looked, nothing there"
if (exists $cache{user_42}) {
    # hit — even though the value is undef, we already did the lookup
} else {
    # miss — have to query the database
}

Sparse-array test. Deleting an array element leaves a hole that exists reports as false:

my @a = (10, 20, 30);
delete $a[1];
exists $a[0];                # 1
exists $a[1];                # ""   (hole)
exists $a[2];                # 1
exists $a[99];               # ""   (past the end)

Subroutine existence. True once the sub has been declared, even if the body is a forward declaration with no definition yet:

sub later;                   # forward declaration
exists &later;               # 1
defined &later;              # ""   (no body yet)

sub later { 42 }
defined &later;              # 1

Nested structure — the safe way. Check each level before descending, because the arrow-chain form has a side effect (see Edge cases):

if (exists $tree{users}
    && exists $tree{users}{$id}
    && exists $tree{users}{$id}{email}) {
    send_mail($tree{users}{$id}{email});
}

Edge cases#

  • Autovivification of intermediate levels. This is the single most important pitfall. exists $ref->{A}{B}{C} tests the innermost key, but evaluating the expression autovivifies every intermediate hash — $ref->{A} and $ref->{A}{B} spring into existence as empty hashes even when the exists test returns false:

    my %h;
    exists $h{a}{b}{c};          # returns "" (false)
    exists $h{a};                # now returns 1 — $h{a} was created!
    exists $h{a}{b};             # likewise — now an empty hash
    

    The innermost element is the only one that does not autovivify. The same happens through references: exists $ref->{A} on an undef $ref vivifies $ref into a hash reference. If you need to probe a deep structure without mutating it, walk it one level at a time with exists at each step, or use a dedicated helper like [Data::Diver] semantics (a chained eval / no autovivification pragma scope).

  • exists on a key with value undef is true. This is the defining difference from defined. delete removes both the key and the value; assigning undef keeps the key.

  • Array bounds. exists $a[$i] is false when $i is past the end of the array or when the slot has been deleted mid-array. Negative indices count from the end: exists $a[-1] tests the last element if the array is non-empty.

  • Array exists is discouraged upstream. The notion of deleting an array element is not conceptually clean — delete $a[1] leaves a hole rather than shifting. Most code that reaches for exists $a[$i] is better served by a bounds check ($i <= $#a) or by using a hash.

  • exists &sub() is a syntax error. The argument must name a subroutine, not call one. exists &sub asks “is sub declared?”; exists &sub() would mean “does the return value of calling sub exist?”, which is meaningless.

  • AUTOLOAD does not count. exists &sub is false for a sub that would spring into existence via AUTOLOAD on first call. A failing exists therefore does not guarantee a call would fail.

  • Tied variables. For tied hashes and arrays, exists calls the EXISTS method on the tied object. The tie implementation decides what “existing” means — a DBM tie checks the on-disk record, a lazy tie might materialise the entry. See perltie.

  • The argument must be an lvalue expression whose final operation is a key or index lookup, or a subroutine name. exists func(), exists $scalar, exists @array, exists %hash are all errors. Only element access and &sub are accepted.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • defined — asks whether the value is usable, not whether the key is present; differs from exists only when the stored value is undef

  • delete — removes an entry so that exists on it becomes false; the counterpart to exists

  • each — iterates key/value pairs of a hash; only visits keys for which exists is true

  • keys — returns the list of existing keys; exists $h{$k} is equivalent to asking whether $k is among keys %h, but O(1) instead of O(n)

  • ref — classifies what a reference points to; useful before exists on $ref->{...} if you are not sure $ref is a hash reference

  • scalar — force scalar context; note that exists is already scalar-only and does not need it