There’s a reason many projects ban the go keyword in favour of a wrapper that fixes some of this.
Go requires constant vigilance, sadly.
But saying it "requires constant vigilance" is an overstatement. As long as you put a little bit of thought in the concurrent code you're writing, it's very easy to do things right. Data races are easy to avoid if you only transfer ownership by sending pointers through channels.
The reason Go annoys people is the unforced errors. Sure, it gets a lot right. But what it gets wrong had solutions long before Go existed and those solutions were wilfully ignored.
Which would interfere with Go's philosophy that zero should be a valid (usable) state for types. What would be the default value of a reference type without nil?
> separate goroutine address spaces
This is not trivial to implement. The only way I can think of is to start each goroutine in a new process, which brings along other downsides - everything must be copied, cannot pass file/socket handlers between goroutines, etc. Doesn't seem to be worth it.
> crash the process on unhandled panic in any goroutine
Go does crash the process on unhandled panic in any goroutine:
# main.go
package main
func main() {
go func() {
panic("hehe")
}()
for {
}
}
# go run main.go
panic: hehe
goroutine 5 [running]:
main.main.func1()
/[...]/main.go:5 +0x27
created by main.main
/[...]/main.go:4 +0x25
exit status 2
> The reason Go annoys people is the unforced errors.This is the criticism I've also had, and raised an issue on GitHub. Unfortunately, it's impossible to fix this without breaking backwards compatibility.
> But what it gets wrong had solutions long before Go existed and those solutions were wilfully ignored.
They were wilfully ignored, but only because the tradeoffs didn't seem worth it at the moment. You make it sound bad, as if Go devs were simply high on crack and knew what they doing was wrong, but did it anyways, when in reality it's more of a case of those "solutions" not being compatible with the goals of the language, or simply nobody coming up with an elegant way to incorporate it.
... but nil is not actually a usable reference. You can't do anything with it that you can do with a real reference.
The whole zero values for every type might seem neat philosophically, but never struck me as sensible.
Separate address spaces are trivial when you have a runtime, just look at Erlang.
Go could’ve also copied Erlang’s supervision trees to ensure safe and bounded goroutine lifetime.
For all their impressive achievements, Go’s creators suffer from extreme NIH syndrome. Just look at Plan 9.
In practice, where I work we disallow the go keyword in review and require use of a wrapper that takes in a closure and also is a wait group. It’s much harder to get wrong.