func hello() int {
if true {
return 0
} else {
return 1
}
}
>go run func.go
./func.go:3: function ends without a return statement func hello() int {
if cond {
return 0
}
return 1
}
anyway. 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.
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.They were wrong and probably not as experienced as they/you thought. Guard clauses make code simpler and more understandable.
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...
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.
[1] http://martinfowler.com/refactoring/catalog/replaceNestedCon...
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.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 find that multiple return statements in a function are more often a symptom of ugly code instead of the reason.
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.
Can actual products (Web apps in my case) be built with Go?
> "Not only is creating an App Engine application easy, it's free! You can create an account and publish an application that people can use right away at no charge, and with no obligation. An application on a free account can use up to 1 GB of storage and up to 5 million page views a month. When you are ready for more, you can enable billing, set a maximum daily budget, and allocate your budget for each resource according to your needs."
[1]: https://developers.google.com/appengine
[2]: https://developers.google.com/appengine/docs/whatisgoogleapp...
If I remember correctly Google switched their download service to using Go and there was a post here not long ago claiming they went from a lot of servers to merely 2 by switching to Golang.
See http://golang.org/doc/articles/wiki/ for an example.
And what might be the reason for that? Speed? Parallel processing?
I'm thinking something along the lines of honeybadger/hoptoad/etc -- automatic notification if the program crashes/deadlocks/what-have-you.
But I'm still unhappy that using named return values still requires you to put a superfluous "return;" at the end of such a function.
The whole reason I use named return values is to cut down on the boilerplate code that carries the return value around -- so why not cut out the final boilerplate 'return'? It doesn't solve any problem or add any information!
Otherwise, there are some nice improvements here. Other than the things mentioned so far, I'm glad to see reflection filling out -- the day a Go REPL will be possible is approaching.
// Match checks whether a textual regular expression
// matches a byte slice. More complicated queries need
// to use Compile and the full Regexp interface.
func Match(pattern string, b []byte) (matched bool, error error) {
re, err := Compile(pattern)
if err != nil {
return false, err
}
return re.Match(b), nil
}
If the final line is missing, the compiler rejects the function. You might well have meant 'return', which could be inserted implicitly, but you might also have been interrupted while writing the function and meant to write a 'return something'. The compiler (really the language) requires you to be clear to avoid inferring an incorrect completion.One nit: the return rules are separate from escape analysis, which is about keeping things on the stack instead of allocating them on the heap.
This one weird simple trick could work (and give you white teeth): implicit returns only get inserted if all the named return variables are assigned-to at least once in the body of the function. Perhaps that breaks your pure syntactic rule, though.
2. Yup, wrong term.
Very close indeed! Struct, array and function types still can't be constructed at run time, but as of Go 1.1, function values can be as well as slice, map and channel types. I exploit some of this in my `ty` package. [1,2]
[1] - http://godoc.org/github.com/BurntSushi/ty
[2] - http://blog.burntsushi.net/type-parametric-functions-golang
I tried to convince rsc at some point about the enormous value of REPLs for rapid development.. imagine connecting to an embedded REPL on a server to do live debugging. I don't think I quite convinced him to drop everything and do it himself, though.
Do you have plans to experiment with a transpiler to smooth away all the remaining type noise?
Maybe a first step would be to explore how one could add pluggable 'dialect translators' to the go tool so that anyone could write simple extensions for the language while preserving the existing toolchain.
A REPL is possible even today.
All is needed is an interpreter for the language and there are a few already available.
Nothing new, this is the same approach taken by many languages, provide an interactive environment for the REPL and a compiler for distribution.
Being done since the early days of computing.
Also I slightly disagree with you that named return values are for short code. This may be true in functions that return at many points. For other functions it may need some otherwisely added boilerplate. If you write inside an if-block f, err := os.Open(...) that err is a different one than your return value err. So you would need to add var f *os.Open above the if to write f, err = os.Open(...).
Having that said, IMHO the greatest pro of named return values is well understandable code, in particular for libraries.
2. You're right, they're not only for short code. That however is where the forced "return" is most glaring.
Because race conditions are so hard to detect, the race detector is obviously prone to false negatives. Just because the tester doesn't find any race conditions doesn't necessarily mean that there aren't any. But the race detector never finds false positives. If it finds a race condition, that condition is very real.
You could just run your app with the race detector on all the time, but there is a performance cost to using the race detector.
One way to get around this in cloud/clustered environments is to deploy your app on a few machines with the race detector on and the rest with the race detector off. That way you're running your app with a production load and you're more likely to find race conditions, but you'll mitigate the performance costs associated with the race detector.
Running the detector on just a few nodes sounds like a great way to offset the performance penalty a bit. The docs on the race detector say that "memory usage may increase by 5-10x and execution time by 2-20x" which could be quite significant.
I also wonder about the effectiveness of randomly fuzzing your app with the race detector on as a form of testing.
https://code.google.com/p/data-race-test/wiki/ThreadSanitize...
Intended, yes. But that doesn't mean we also develop "cleanly&properly" at all times, in the real world. It's a great "backup" for when formerly-prototyping-now-production code is getting slightly out of hand over time.
1outyet.com and make a subdomain, so it could be
isgo1.1outyet.com ?
It was a demo in a talk by @enneff at Railsberry 2013: https://github.com/nf/go11
The site should update automatically once the repository is tagged.
I kinda figured they wouldn't leave the polling time at 5 seconds.
"It's worth mentioning the function representation change, since it means closures no longer require runtime code generation (which among other things allows the heap to be marked non-executable on supported systems)."
Anyone know what that means?
Edit: dylanvee was faster, the NX-bit is the underlying hardware feature
TL;DR: functions are now represented as (function pointer, pointer to a context structure), whereas before they were represented as a single function pointer.
If you are worried about leaking goroutines,
import _ "net/http/pprof"
in your web server and then visit /debug/pprof/goroutine (or even just /debug/pprof). That listing shows all the active goroutine stacks, but it groups bunches with the same stack into a single entry with a count. Scanning the list it is usually easy to see leaks (hey, why do I have 5000 goroutines with that stack) and also why they are stuck (because you have the whole stack).Outside of the "dangling reference" issue above "leaking" in a GCd language is non-trivial. Here is a related Java discussion: http://stackoverflow.com/questions/6470651/creating-a-memory... . Also note Go doesn't have ClassLoader or class static fields.
So, you can pretty easily leak memory without messing with unsafe things.
No, gccgo has been developed as a first-class compiler; the intent has always been to separate Go-the-language from Go-the-implementation, to prevent a single implementation from becoming the de facto standard over the language specification - a problem which we've seen happen in many other languages.
You can essentially use gccgo as a drop-in replacement for gc; it's only an extra command-line flag you add to the compilation to specify the compiler.
gccgo is actually likelier to be faster than gc for most computationally bound code, since it piggybacks off of the optimizations that gcc has incorporated over the last couple of decades. However, gc may be preferred if you're relying heavily on goroutines (ie, in the hundreds/thousands), since gc is better optimized for that.
It would also be nice to have a first-class array object - from what I understand, current support for multidimensional arrays is very C-like in that you're really dealing with pointer arrays.
I agree about the numerics. I'm tempted to write an very bare-bones implementation of Mathematica's kernel in Go, but I'll probably wait until people have done more numerics work in Go.
And I'm glad that they're bumping up the heap size to system-dependent values on 64bit systems. The fixed heap size of 16 GB was a really unfortunate constraint (even if it could be changed with a little hacking around).
other issues: probably not, since these are new issues that are being worked on