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.
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.
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 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.
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.
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.
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.
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.
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.
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.
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.