mldvx4 has asked for the wisdom of the Perl Monks concerning the following question:

What is the correct way to pass a hash to a subroutine separately from a few strings? I have been using the following method,

sub generate { my $record = shift; my $status = shift; my %data = %{$_[0]}; ...

However, that brings up an "Always unpack @_ first" error with perlcritic --stern and therefore I wonder if there is a more appropriate method.

Replies are listed 'Best First'.
Re: Passing a hash plus some strings to a subroutine
by Fletch (Bishop) on Dec 17, 2025 at 15:02 UTC

    I'd maybe do something like this, or just name it $data and use $data->{'foo'} rather than having to have an actual hash locally (unless you're worried about altering the original through the ref).

    sub generate { my( $record, $status, $_data ) = @_; my %data = %{ $_data }; ... }

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      Thanks. By the time the data gets that far into my script, there is no intention to change it. However, I think for readability, I'll go with the approach which creates the local hash.

Re: Passing a hash plus some strings to a subroutine
by hippo (Archbishop) on Dec 17, 2025 at 15:25 UTC

    It's just moaning about your use of individually shifting the args. This passes perlcritic --stern for me:

    sub generate { my ($record, $status, %data) = @_; return; }

    🦛

      Nothing wrong with shifting. Easier to provide defaults and comments. And then there's the newish signature feature. Take perlcritic's critiques with a grain a salt, and adjust them to your liking.

      Not the same thing tho.

      The OP is passing a hashref, but you are un- and repacking a hash as list of keys and values.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      see Wikisyntax for the Monastery

      Why does perlcritic complain about this:
      my %arg = @_;
      Always unpack @_ first...

        It does not complain about that. Here is my SSCCE showing as such:

        $ cat x.pl #!/usr/bin/env perl use strict; use warnings; use utf8; sub foo { my %arg = @_; print "Woo!\n"; return; } foo (z => 3); $ perlcritic --stern x.pl x.pl source OK $

        This suggests that the cause of its complaint (if any) is in the code which you have not shown, such as if you have done something in the sub before that line and hence that line is not "first".


        🦛

Re: Passing a hash plus some strings to a subroutine
by LanX (Saint) on Dec 17, 2025 at 16:11 UTC
    It depends on ...

    • which version of perl you are using
    • how much you care about backwards compatibility
    • if this semantic matters (passing a hashref and flat-copying it)
    update

    but yeah, the real problem here is perlcritic enforcing old PBP rules here.

    Not sure how it handles newer mechanisms.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

      but yeah, the real problem here is perlcritic enforcing old PBP rules here.

      Is there another tool which would be more appropriate to run?

        perlcritic is fully configurable. If you disagree with a policy, you can turn it off.

        See also Perl::Critic::Community for a revised and modernised set of perlcritic rules, created mostly by the IRC subset of the Perl community.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
        Sorry, I'm the wrong one to ask about Perl::Critic - I don't use it.

        I know that many companies expand/restrict the rule sets to their liking in order to have coherent internal style.

        And PBP itself described its rules only as suggestions, IIRC.

        Anyway, what Fletch showed you is the standard vanilla approach which will always work.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Re: Passing a hash plus some strings to a subroutine
by haukex (Archbishop) on Jan 11, 2026 at 08:51 UTC

    Perl::Critic is great and I use it for all my modules. However, it's only advice, and can be configured to the author's tastes. The intent of the RequireArgUnpacking rule, which is what you're seeing here, is to catch subroutines that use @_ throughout their code instead of unpacking it into local variables at the beginning of your sub. However, your code is doing that here, so the rule is simply being a little too trigger happy. This is exactly the reason you'll find  ## no critic (RequireArgUnpacking) scattered throughout my code, and that's my advice for you in this case.