Re: [go-nuts] Bare return statements in functions

769 views
Skip to first unread message

minux

unread,
Nov 1, 2012, 4:58:53 PM11/1/12
to zda...@gmail.com, golan...@googlegroups.com

On Fri, Nov 2, 2012 at 4:01 AM, <zda...@gmail.com> wrote:
I am curious to know what the community feels about bare return statements.  I personally love them.  While reading a book on Go, it was mentioned that using bare return statements is of bad form.  To clarify...

//Contrived function
func divThem(a, b int) (x int, err error) {
   if b == 0 {
      err = fmt.Errorf("Sorry, no divide by zero")
      return
   }
   x = a/b 
   return
}

I personally find named "err" extremely useful.
One drawback for bare return statements, the it's hard to find out the actual value returned for
each return value. You effectively have to follow all paths of execution to be sure of which value
is returned.

Michael Jones

unread,
Nov 1, 2012, 5:04:11 PM11/1/12
to minux, zda...@gmail.com, golan...@googlegroups.com
...which is generally bad but can be good/natural in cases where a
function finds/computes a collection of independent values..
> --
>
>



--
Michael T. Jones | Chief Technology Advocate | m...@google.com | +1
650-335-5765

Dustin

unread,
Nov 1, 2012, 5:10:32 PM11/1/12
to golan...@googlegroups.com

On Thursday, November 1, 2012 1:01:24 PM UTC-7, Zack Dannar wrote:
Hello,

I am curious to know what the community feels about bare return statements.  I personally love them.  While reading a book on Go, it was mentioned that using bare return statements is of bad form.  To clarify...

//Contrived function
func divThem(a, b int) (x int, err error) {
   if b == 0 {
      err = fmt.Errorf("Sorry, no divide by zero")
      return
   }
   x = a/b 
   return
}

   I'm generally offended by having to type "return" at the end of a function as if something else could happen. 

Dustin

unread,
Nov 1, 2012, 5:13:08 PM11/1/12
to golan...@googlegroups.com, zda...@gmail.com

On Thursday, November 1, 2012 1:59:22 PM UTC-7, minux wrote:

One drawback for bare return statements, the it's hard to find out the actual value returned for
each return value. You effectively have to follow all paths of execution to be sure of which value
is returned.

  This isn't too bad:

func something(in int) (out int) {
defer func() {
fmt.Printf("something(%v) -> %v\n", in, out)
}()
out = in
if in%111 == 0 {
return 3
}
return
}


Michael Jones

unread,
Nov 1, 2012, 5:14:10 PM11/1/12
to Dustin, golan...@googlegroups.com
It is a temporary compiler work-around for those cases where the
return has already happened.

:
if true { return 1 } else { return }
return // mandatory for the moment
}

On the broader sense, though, it seems like the lexical "stick in a
';' logic could be expanded to "when open close brace matching tells
you that this '}' closes a function, insert ";return" just before if
the last statement was not 'return'

Dustin

unread,
Nov 1, 2012, 5:18:37 PM11/1/12
to golan...@googlegroups.com, Dustin

On Thursday, November 1, 2012 2:14:54 PM UTC-7, Michael Jones wrote:
It is a temporary compiler work-around for those cases where the
return has already happened.

:
if true { return 1 } else { return }
return // mandatory for the moment
}

  You can replace that last line with `panic("this can't happen")`
 
On the broader sense, though, it seems like the lexical "stick in a
';' logic could be expanded to "when open close brace matching tells
you that this '}' closes a function, insert ";return" just before if
the last statement was not 'return'

  You don't have to type "return" if there aren't any declared results.  If they're named, they're already there.  You just have to place that word at the end of a function to declare that you've set the variables and you're done now.  I see no other rational explanation for what should happen at the end of a function without that line.

  I've had similar arguments in other languages with other functions that don't have return values, but someone thinks it's "clearer" to say "return" at the end.  Because there's some other possibility that might confuse a casual programmer in every other function in the codebase and he'd only be comfortable upon looking at this one thing that properly declares the function returns and doesn't continue executing into the next function or something. 

Kyle Lemons

unread,
Nov 1, 2012, 5:31:05 PM11/1/12
to Zack Dannar, golang-nuts
if you like to avoid typing, then bare returns are for you. However, if you like reading code, you will quickly find that bare returns hamper readability in all but the simplest cases.  In code reviews, I encourage explicitly stating what to return when the return statement is in a conditional or a loop and tend to discourage bare returns in general.


On Thu, Nov 1, 2012 at 1:01 PM, <zda...@gmail.com> wrote:
Hello,

I am curious to know what the community feels about bare return statements.  I personally love them.  While reading a book on Go, it was mentioned that using bare return statements is of bad form.  To clarify...

//Contrived function
func divThem(a, b int) (x int, err error) {
   if b == 0 {
      err = fmt.Errorf("Sorry, no divide by zero")
      return
   }
   x = a/b 
   return
}

I personally find named "err" extremely useful.

Thanks in advance.

--
 
 

Zack Dannar

unread,
Nov 1, 2012, 5:44:25 PM11/1/12
to golan...@googlegroups.com, Zack Dannar
That seems reasonable.  There may be something that I am missing though.  If you have multiple return arguments from a function you would then have to default or put the named return args in the return statement.  For example:

func dosomething() (x int, y int, somePtr *something, err error) {
    x,err  = another()
    if err != nil {
          return
    }

   if x == -1 {
        return  x, y, nil, err        // Could replace nil with with SomePtr
   }

   .....  // more of the function
}

Would you return the zeros of the type or return the named arguments which are zeroed already?


** Please note, I haven't check to see if something like this will compile.

Thanks

Dustin

unread,
Nov 1, 2012, 5:48:25 PM11/1/12
to golan...@googlegroups.com, Zack Dannar

  Brad posted some guidelines for named return values somewhere.  It'd be good to dig that up... something about being good when it's public API and it makes the docs clearer.

  The takeaway I got from it is not to put a lot of weight in the "my code is slightly easier to write" bucket.

Rory McGuire

unread,
Nov 2, 2012, 4:08:10 AM11/2/12
to golan...@googlegroups.com
I really like bare returns but its WAY to easy to get it wrong especially when using := and err.

I made an example, its not a good example but I've already had to fix other peoples libraries I'm using 
that using bare returns which is what I based this little example on.

Basically its only safe to use bare returns if you never use them in := statements.

Dustin

unread,
Nov 2, 2012, 4:12:35 AM11/2/12
to golan...@googlegroups.com, Zack Dannar, ry...@heatery.com

On Thursday, November 1, 2012 2:56:32 PM UTC-7, ry...@heatery.com wrote:
Insteresting. Dustin, do you have a link?


And repeated for ease of reading:

+Brad Fitzpatrick recently posted his thoughts on named return values to an internal list. I agree with them entirely, and thought them worth repeating here:

-- if it's an unexported function, I don't care.
-- if it's a exported, avoid stutter because it pollutes the godoc.

this adds no clarity to the docs, only stutter:

func (n *Node) Parent() (node *Node)
func (n *Node) Parent() (node *Node, err error)

These are more readable:

func (n *Node) Parent() *Node
func (n *Node) Parent() (*Node, error)

On the other hand, what are these?

func (f *Foo) Bar() (string, string, error)
func (f *Foo) Bar() (string, int, error)
func (f *Foo) Bar() (string, bool) // is this a comma-ok signature? or is the bool something else?

-- Name comma-ok signatures:

func (f *Foo) Bar() (name string, ok bool)

-- Naked returns are okay if the function is a handful of lines. Once it's a medium sized function, be explicit with your return values. Corollary: it's not worth it to name result parameters just because it enables you to use naked returns. Clarity of docs is always more important than saving a line or two in your function.
 

Kevin Gillette

unread,
Nov 2, 2012, 9:31:23 AM11/2/12
to golan...@googlegroups.com, zda...@gmail.com
On Thursday, November 1, 2012 2:59:22 PM UTC-6, minux wrote:
One drawback for bare return statements, the it's hard to find out the actual value returned for
each return value. You effectively have to follow all paths of execution to be sure of which value
is returned.

I disagree with that notion altogether. You're implying that explicit return values using variables don't require you to trace all (or at least the pertinent) paths of execution to find out what the returned _values_ will be. Only returning constants (e.g. `return 5, nil`) don't require you to trace paths of execution for this purpose.

Bare return values corresponding to named return variables have the following characteristics:
* Can look in one place (the func signature) to find the _names_ of the expected return variables.
* Must trace pertinent paths of execution to determine the returned values.
* When tracing, must be aware of shadowed names, e.g. using short decls, which may lead to the expected _shadowing_ vars not being used when returning.
* A faster trace of the value(s) can work from outer scopes to inner scopes (if and when a variable is shadowed, tracing can stop before that inner-scope boundary).

Explicit _variable_ return values have the following characteristics:
* Can look in one place (the return statement) to find the _names_ of the expected return variables.
* Must trace pertinent paths of execution to determine the returned values.
* When tracing, must be aware of shadowed names in inner scopes, which may lead to the _shadowed_ variables not being modified as expected.
* A quick determination of the variable(s) used in the return can work from inner scopes to outer scopes (if and when a variable is shadowed, tracing can stop before that outer-scope boundary).

In both scenarios, the complexity involved in fully understanding the execution is the same -- explicit returns have the opposite (but no more simple) nature to bare returns in some cases, and the same characteristics in others.

Kevin Gillette

unread,
Nov 2, 2012, 9:38:30 AM11/2/12
to golan...@googlegroups.com, Dustin
On Thursday, November 1, 2012 3:18:38 PM UTC-6, Dustin wrote:
  You don't have to type "return" if there aren't any declared results.  If they're named, they're already there.  You just have to place that word at the end of a function to declare that you've set the variables and you're done now.  I see no other rational explanation for what should happen at the end of a function without that line.

  I've had similar arguments in other languages with other functions that don't have return values, but someone thinks it's "clearer" to say "return" at the end.  Because there's some other possibility that might confuse a casual programmer in every other function in the codebase and he'd only be comfortable upon looking at this one thing that properly declares the function returns and doesn't continue executing into the next function or something. 

A good explanation for what could happen at the end of a function aside from returning is "under construction/not yet implemented". Manually typing a return statement at the end (or a panic) denotes either a conscious approval of the function being complete (and that a return really does belong there), or of a programming workflow wherein functions are copied/pasted from templates (why?), written from the outside in, written in reverse, or written in no particular order at all.

In any of these cases, it would be a very bad thing to have an implicit return statement at the end of any function, since it will allow some things to pass through a compilation check that really weren't meant to be ready for compilation (and yes, some people do use the compiler as a 'todo list').

Rob Pike

unread,
Nov 2, 2012, 10:54:00 AM11/2/12
to Kevin Gillette, golan...@googlegroups.com, Dustin
If they make the code better, use them. If they don't, don't.

-rob

minux

unread,
Nov 2, 2012, 10:55:04 AM11/2/12
to Kevin Gillette, golan...@googlegroups.com, zda...@gmail.com
you've proved that in the worst case of explicit return is no better than bare returns.
Yes, of course, as you can always do this:
func f() (int) {
   retVal := 0
   // a very long function body
   return retVal
}
so that explicit returns are obviously no better than bare returns.

However, this is not how we use explicit returns.
my observation is:
1. when using bare returns, the last assignment to the returned value tends to be far away
from the return, i guess the reason is that if they are very close, it makes more sense to
use explicit return and saves an assignment.
2. when using explicit returns, even if we are returning a variable, the (last) assignment to
that variable is almost always immediate preceding, or within 2-5 lines of, the return statement,
and more often than not, the value returned is not a simple variable but an expression.

Kevin Gillette

unread,
Nov 2, 2012, 11:30:37 AM11/2/12
to golan...@googlegroups.com, Kevin Gillette, zda...@gmail.com
I agree with you there. The best case for bare returns, as well as the best/common case for explicit returns is far from the worst case.
Reply all
Reply to author
Forward
0 new messages