Among the many warnings of Perl that might, or might not indicate a bug, this certainly points to code that was written incorrectly.

If we run this script:

examples/hash_with_or.pl

use strict;
use warnings;

my %h = (
    a => 1,
    b => 2,
);

my $r = $h{a} or $h{b};

We get: Useless use of hash element in void context

The same is true if we use HASH references:

examples/hashref_with_or.pl

use strict;
use warnings;

my $x = {
    a => 1,
    b => 2,
};

my $r = $x->{a} or $x->{b};

The problem was probably created when the author of this code wanted to set a default value. That is the author wanted to set $r to be equal to $h{a}, but if that key did not exist, or if its value was undef then she wanted to set $r to be $h{b}.

Unfortunately the snippet to set default value uses || and not or.

The reason for that is that || is higher in the precedence table than = which is higher than or. So the correct code would have been:

my $r = ($h{a} or $h{b});

or in a more idiomatic way:

my $r = $h{a} || $h{b};

Probably even better to use the defined-or operator:

my $r = $h{a} // $h{b};

that was introduced in Perl 5.10.

B::Deparse

If you did not know the above and could not find an article explaining it, you could always ask Perl to tell you what does it think about a piece of code. For this you can usually use B::Deparse with the -p flag to add extra parentheses.

In our case this is what we get:

perl -MO=Deparse,-p examples/hash_with_or.pl

Useless use of hash element in void context at examples/hash_with_or.pl line 9.
use warnings;
use strict;
(my(%h) = ('a', 1, 'b', 2));
((my $r = $h{'a'}) or $h{'b'});

Here you can see that B::Deparse added parentheses around the assignment (my $r = $h{'a'}) which means that will be executed first and then there is a dangling extra code: or $h{'b'} that has no impact on anything. That's why Perl warns you about useless use.

The correct way to write this would be to write this:

examples/hash_with_or_fixed.pl

use strict;
use warnings;

my %h = (
    a => 1,
    b => 2,
);

my $r = $h{a} // $h{b};

Comments

I have been bitten by this, as I tend to default to “or” because it is lower precedence, which often is desired, as in this case:

open FH, $file or die “Cannot open file $file”;

If you use || instead of “or”, the double pipe resolves first, and so will return true for a non-empty file. But since “or” has a lower precedence than comma, if you use “or”, it will execute as expected.

Note that confusion between “or” and || can be avoided enforcing precedence with parentheses, by writing:

open ( FH, $file ) || die “Cannot open file $file”;

making parentheses a good standard practice in my book.

I think a thorough discussion of this in the Beginner Perl Maven e-book would be an excellent addition (if it is not already there).