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

A colleague came up with the curious construction
sub fn { return: 3 }
And it surprised me that it does exactly what he expected it to do: it returned '3'. I had expected that 'return:' would label a block, which, containing only '3', would be evaluated true, and '1' would therefore be returned.

What's actually going on here, and what side-effects might it have? Does the label get evaluated at all? Given that 'return' does little other than declare the end of the subroutine, is there really anything wrong with his usage?


- Boldra

Replies are listed 'Best First'.
Re: 'return:' instead of 'return'
by afoken (Chancellor) on Jun 12, 2009 at 09:11 UTC

    I think you are really creating a label named return here, followed by a constant expression. As that expression is the last expression evaluated, it is used as return value.

    Look at this code: It returns 5 and gives a "Useless use of a constant in void context" warning:

    sub foo { return: 3; return 5; }

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      "Useless use of a constant in a void context"

      Sorry, that's an artefact of my test code, actually what he was doing is more like:
      sub cmd { my ($cmd) = @_; $cmd =~ s/\//\\/ig; my $ret = system($cmd); return: $ret; }
      which is horrible for other reasons, but it doesn't produce the above warning. It was only thanks to perltidy that I noticed that there was something funny going on on the last line.


      - Boldra
        but it doesn't produce the above warning
        That is because in your code, it's the last statement in the sub. So by default, it's the value that is returned, even without a real return.
Re: 'return:' instead of 'return'
by Corion (Patriarch) on Jun 12, 2009 at 09:12 UTC

    The construct is interpreted as a label, as the following code shows:

    sub fn { return: 3; print 'code after return?' }; fn()

    As the return value of a function is the last expression evaluated, the value of your constant block, 3, is returned.

Re: 'return:' instead of 'return'
by JavaFan (Canon) on Jun 12, 2009 at 09:19 UTC
    The return is indeed a label. But labels don't label blocks, they label statements. A block is a form of a statement, but not all statements are blocks. In this case, the statement is 3. But even it it were a block, blocks don't return true/false. The return value of a block is the value of the last statement executed.

    Does the label get evaluated at all?
    Labels never get "evaluated".
    Given that 'return' does little other than declare the end of the subroutine, is there really anything wrong with his usage?
    'return' (as a statement) doesn't declare the end of a subroutine, it's just a way to exit the subroutine. You can have a return statement halfway the subroutine. Whether there's something "wrong" depends on what you mean by it. If 'fn' is supposed to return '3', then it will, regardless whether the label is there. But this is not a general return statement.
    sub fn { if (1) { return: 3 } 2 }
    is not going to return 3. It will return 2. So it that sense, the use to 'return:' is wrong.
      Thanks for the explanation. If I understand correctly, by default the result of evaluating the last statement is returned when no explicit return is provided, and since declaring a label is not a statement, it doesn't get evaluated. I had expected something more like the behaviour from this:
      sub fn { sub { 3 } }
      Where it's not the 3 that gets returned, rather it's a coderef to an anonymous subroutine.

      As for 'wrong', you've guessed my meaning well. If the code is likely to mislead someone making changes at some point in the future, it is 'wrong' now. Since the 'return:' label doesn't cause the subroutine to return, putting code after it could have unexpected side-effects for anyone mistaking it for a normal 'return'.

      BTW, is 'return 3;' a statement? If so, is it then a special exception to the rule 'blocks return the value of the last statement executed', or is the value of 'return 3' equal to 3 ?


      - Boldra
        I had expected something more like the behaviour from this:
        sub fn { sub { 3 } }
        Where it's not the 3 that gets returned, rather it's a coderef to an anonymous subroutine.
        Are you suggesting you didn't even try to run:
        sub fn { return: 3 }
        and see what it returns? Printing the return value of fn would have instantly revealed fn had returned '3', and not a code reference.
        BTW, is 'return 3;' a statement?
        Most certainly. What else could it be? It's not some pretty decorations.
        If so, is it then a special exception to the rule 'blocks return the value of the last statement executed'
        It's a statement. But it's not a block.
        is the value of 'return 3' equal to 3 ?
        sub foo {return 3;} print foo(); __END__ 3
        So, what do you think?

        BTW, is 'return 3;' a statement? If so, is it then a special exception to the rule 'blocks return the value of the last statement executed', or is the value of 'return 3' equal to 3 ?

        No exception. That's indeed what a block returns, and the same goes for subs. They return the the value of the last statement executed.

        sub foo { if (0) { # <- Last statement executed 1 } } # Foo returned 0.
        sub foo { if (2) { 3 # <- Last statement executed } } # Foo returned 3.
        sub foo { return 4; # <- Last statement executed 5 } # Foo returned 4.
Re: 'return:' instead of 'return'
by BrowserUk (Patriarch) on Jun 12, 2009 at 09:15 UTC