Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

They finally fix:

  func hello() int {
      if true {
          return 0
      } else {
          return 1
      }
  }

  >go run func.go
  ./func.go:3: function ends without a return statement


It's nice that they fixed it, but I would always write

    func hello() int {
        if cond {
            return 0
        }
        return 1
    }
anyway.


The reason I don't like this way is it can be harder to refactor. You don't want "return 1" happening if cond. For example, if you refactor to something like this:

    func hello() int {
       var result int
       if cond {
          result = 0
       }
       result = 1
       
       // New code with result

       return result
    }
      
Now, in this case, you don't want result to be 1 if cond, so you have to add the else condition. If you start with if-else, this is less likely to bit you in the future.

This particular bug just bit me in a bad way in production because I had what you have and and to make an quick production fix and did a refactor just like this and missed adding the else.


When you do a lot of I/O you always have err return values included. For that I find the style without else much more convenient.


In languages with ternary I would do this (I agree with Go's decision to remove it but in a case this simple, I'd use it if it were there)

  int hello() {
    return cond ? 0 : 1
  }
In Go, I'd do this, but then I seem to like named return values more than most Go programmers...

  func hello() (res int) {
        if !cond {
            res = 1
        }
        return
    }
Of course, coming from C/C++, it would have to be an extremely special case for me to have logic where "true" mapped to 0 and "false" mapped to 1, because that just seems wacky.


I disagree with your use of named returned values for something like that.

https://plus.google.com/106356964679457436995/posts/LmnDfgeh...

EDIT: Sorry, let me explain (I'm not an asshole, really!). I disagree with using named returned for things outside of signaling error/ok states (as explained by Andrew). I feel that our signatures should be written concisely for users of our API, not for our convenience.


Yeah, as do other other Go programmers I know of, which is why I said "I seem to like named return values more than most Go programmers".

I respect Andrew Gerrand and Brad Fitzpatrick quite a lot but I still often use named returns on even small functions. I find doing so usually makes the actual function code more concise and easily readable for me and I don't think the negative impact on the docs is significant. IMO auto-generated go-docs have far worse problems than the 'noise' from named returns, I think they suffer a lot more from core language decisions like the flexible interface system. And to be clear, I think the interface system in Go is brilliant and I love using it, but I also think it makes auto-generated go-docs hard to digest (and use as quick references) in a way that auto-generated OOP language docs (javadoc, doxygen from C++, etc) aren't.


Back when I was in college doing Java, Eclipse would throw up an error for unnecessary "else" statements. Ever since then I can't help but write it your way as well.


I've been taught by some pretty experienced engineers that in terms of readability, multiple return statements are a bad idea. Instead, you should conditionally set a return variable, and return it once at the end of the function. But I'm not sold.... what is HN's thought on this matter?


> I've been taught by some pretty experienced engineers that in terms of readability, multiple return statements are a bad idea

They were wrong and probably not as experienced as they/you thought. Guard clauses make code simpler and more understandable.


Guard clauses and single return statements are not mutually exclusive.


The whole idea of guard clauses in algol-derived languages (not functional ones) is to bail out immediately with an early return...

http://martinfowler.com/refactoring/catalog/replaceNestedCon...


I understand that but you can have a macro wrapping a goto statement to a predefined label which will do that for you (and potentially set some errors). It's debatable whether this really gives you anything but I kind of like this style since you can replace the whole if statement with a single line something like check_memory(pointer);. The goto's become particularly useful if you want to have some cleanup done at the end of the function even if something during the function fails.


Now your assuming the language has goto. Which definitely is not the case these days.


Well the discussion is about go and go does have gotos. http://golang.org/ref/spec#Goto_statements


You were misled by engineers parroting the ideology they were taught in school. Else statements are far less readable than straight-line control flow with early returns.


Go discourages it http://golang.org/doc/effective_go.html#if

I think a blanket ban on multiple return locations is silly, as they can often be used to simplify code. There may be times when setting a return value is preferable, and I think you should use your judgement there.

http://stackoverflow.com/questions/36707/should-a-function-h...


The mentality of using a single return statement at the end of a function comes from languages that require memory management. In these languages if you return early you would cause a leak by not releasing your resources at the bottom of the function before returning.

http://programmers.stackexchange.com/a/118717


With go's defer mechanic (defer f.Close(), defer l.Unlock()), and the way they handle errors, multiple returns are basically the way code comes out naturally. I think it's more readable than juggling a bunch more variables and returning at the end, others may disagree.


Logically, a simple if-else return tends to follow three basic forms:

    if x
      return a
    return b

    if x
      return a
    else
      return b

    if x
      r = a
    else
      r = b
    return r
Out of these I find the first is the most prone to maintenance errors. It's easy at a glance to see the final return, insert something in front of it, and miss that it needs to happen on another path. At least in the other two cases the indentation makes it clear that it's a conditional return path and you look for others.

I don't have a problem with a "throw" instead of "return a" in any of the forms because that's expected to be an aborted path anyway. In the case of two returns, maybe it is, maybe it isn't.

It's a small thing but when you read hundreds of thousands of lines of code, every little thing that makes it easier is worthwhile.



I prefer to avoid multiple return statements and follow the single-return-at-the-end rule.

However, I happily make exceptions for:

a) Simple shortcut checks at the top of the function. These tend not to increase the complexity of the control flow and can really simplify it.

    void free(void * p)
    {
        if (!p) return;

        ... rest of function
    }
b) Cases where it's just plain unnatural to do it any other way. This can occur with state machines and complex loops. When I do this, I make sure to put a comment way out on the right.

     void process_bytes(unsigned char * p)
     {
         ...
         for (;;)
         {
            ...
            switch (loop_state)
            {
            ...
            case specific_case:
                switch (input_symbol)
                {
                ...
                case end_symbol:
                    return;                      //----- note inner return
                ...
                }
                break;
            ...
	    }
         }
     }
Before folks jump on me for having nested switch statements or "complex loops" in the first place, let me point out that when I write this type of code it's usually because I'm processing a data format defined by somebody else.


In a question about this on programmers.se, a slightly different history of the "Single Entry, Single Return" mantra is presented: http://programmers.stackexchange.com/a/118793/4025 Essentially, it is argued that the practice that is warned about is to return to different places from the same function, not from different places within the function.

On a separate note, my take is that multiple returns are necessary to write readable understandable code quite often. Guard statements (either handling normal simple boundary cases, or throwing exceptions) at the beginning simplify logic and gives a clean reading of the code.


Only a sith etc etc. Like just about every other blanket statement about programming, this also is sometimes true and sometimes not.

I find that multiple return statements in a function are more often a symptom of ugly code instead of the reason.


I agree with the other commenters, but I'll say that I understand the original intent of the rule was to avoid confusing logic, such as:

if (x): do(thing1) y := do(thing2) if (y): do(thing3) return 0 else: return -1 else: do(thing4) return 0

___

As you can see, such logic could quickly become hard to test and reason about. Does a single return help all that much? Not in and of itself, but it does tend to make writing such code a bit more painful, leading to better designs. However, guard clauses are a superior design in general.

I still avoid multiple returns in my main logic when side effects are involved, at least when I can.


I used to follow a bunch of best practices like this, that I now often find to be of too little benefit. If the function is small, multiple return statements won't significantly affect readability and it's simpler to code.


>> If the function is small <<

And if the function is not small?


Then extract functions until it's small, a.k.a. refactoring.


...and if you end up needing to pass 20 variables via reference for state?



You have a group of parameters that naturally go together. ...when you have a bunch of methods that call each other, all of which have a clump of parameters that need this refactoring. In this case you don't want to apply Introduce Parameter Object because it would lead to lots of new objects

There are times when this transformation yields simpler code, there are times when it makes things more complex, and there are a lot of cases in between where it's a judgment call.


Then I might set a variable and return that. The goal is to make the function readily understood.


Having a single return point is a pretty good idea in C. In other programming languages, probably not.


Also not handling JSON null values: https://code.google.com/p/go/issues/detail?id=2540


I'm pretty sure that was fixed in both previous betas and tip for some time before that.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: