
Common Mistakes for New Golang Devs - BerislavLopac
http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
======
echlebek
One of the most common mistakes new Go developers make are creating race
conditions.

For example, the author's final example. (And maybe others, I didn't read them
all.)

    
    
      $ go run -race racy.go
      ==================
      WARNING: DATA RACE
      Write at 0x00c42009800f by goroutine 6:
        main.main.func1()
            /home/eric/racy.go:12 +0x38
      
      Previous read at 0x00c42009800f by main goroutine:
        main.main()
            /home/eric/racy.go:15 +0x8b
      
      Goroutine 6 (running) created at:
        main.main()
            /home/eric/racy.go:11 +0x76
      ==================
      done!
      Found 1 data race(s)
      exit status 66
    

edit: add code listing

    
    
      package main
      
      import (  
          "fmt"
          "runtime"
      )
    
      func main() {  
          done := false
      
          go func(){
              done = true
          }()
      
          for !done {
              runtime.Gosched()
          }
          fmt.Println("done!")
      }

~~~
_sdegutis
What should the code be instead?

~~~
TheDong
There are many possible fixes. The most obvious is to wrap it in a mutex
(assuming 'done' is standing in for much more complex logic):

    
    
      func main() {  
          doneMutex := sync.Mutex{}
          done := false
      
          go func(){
              doneMutex.Lock()
              defer doneMutex.Unlock()
              done = true
          }()
    
          getDone := func() bool {
            doneMutex.Lock()
            defer doneMutex.Unlock()
            return done
          }
      
          for !getDone() {
              runtime.Gosched()
          }
          fmt.Println("done!")
      }
    
    

Another option would be to use a channel in place of done.

Effectively, the lesson here is that go doesn't have concurrently safe types
(e.g. no concurrent map) nor generics to create them, so all concurrent code
must be built around the builtin generic concurrent-safe type (channels) or
must make careful use of mutexes / the atomic package / etc.

Idiomatic concurrent go either is built around channels or mutexes, and in
either case the compiler won't tell you if you messed up, only the runtime
race detector and testing will save you.

~~~
gophergopher
Version 1.9 introduced the sync.Map
[https://golang.org/pkg/sync/#Map](https://golang.org/pkg/sync/#Map)

Would using a sync.WaitGroup and closure be wrong? Is mutex better for shared
memory?

~~~
jzelinskie
sync.Map is NOT a good solution for all concurrent map use cases. Please use
your best judgement for the well documented trade-offs:

>The Map type is optimized for two common use cases: (1) when the entry for a
given key is only ever written once but read many times, as in caches that
only grow, or (2) when multiple goroutines read, write, and overwrite entries
for disjoint sets of keys. In these two cases, use of a Map may significantly
reduce lock contention compared to a Go map paired with a separate Mutex or
RWMutex.

------
ebikelaw
"In C++ creating variables using the new operator always means that you have a
heap variable."

It's hard to make accurate statements about C++ :-) This is not one. Perhaps
it would be more correct to say "In C++ a |new| expression is used to
construct an object whose lifetime is not necessarily bound to the scope in
which it is created" or something like that. It is definitely not true that an
object created with |new| is allocated on a heap. It can be placed on the
stack, or any place, or maybe not even allocate.

~~~
marcoperaza
Could you explain?

~~~
Tomte
placement new lets you choose where to construct an object.

It‘s somewhat arcane, though. Few application programmers even know about it
(although their C++ books all dutifully mention it, usually without more than
a single sentence).

~~~
vvanders
It's not that arcane, we used it _all_ the time for pooling, particle systems,
in-place loading, etc. Super handy but also like most things in C++, dangerous
if you don't know what you're doing.

------
snarfy
I tried to approach Go as a better C and alternative to C++. C++ has too many
warts. I was hoping Go fixed them. A lot of them it did, but articles like
this reminds me how many warts Go still has.

> Ken Thompson was once asked what he would do differently if he were
> redesigning the UNIX system. His reply: "I'd spell creat with an e."

Similar naming is littered through Go. It's like he never learned.

~~~
mseepgood
He fixed it in Go:
[https://github.com/golang/go/commit/c90d392ce3d3203e0c32b3f9...](https://github.com/golang/go/commit/c90d392ce3d3203e0c32b3f98d1e68c4c2b4c49b)

~~~
snarfy
And yet we have package names like "fmt".

------
kylequest
Ping me if you have other gotchas you'd like to add. There'll be a few more
soon (JSON, Cgo, etc).

~~~
isaachawley
This is outdated and incorrect [http://devs.cloudimmunity.com/gotchas-and-
common-mistakes-in...](http://devs.cloudimmunity.com/gotchas-and-common-
mistakes-in-go-golang/index.html#close_http_resp_body)

~~~
sargas
How so?

~~~
isaachawley
> On error, any Response can be ignored. A non-nil Response with a non-nil
> error only occurs when CheckRedirect fails, and even then the returned
> Response.Body is already closed.

[https://golang.org/pkg/net/http/#Client.Do](https://golang.org/pkg/net/http/#Client.Do)

------
bogomipz
The articles states:

>"It's OK to call a pointer receiver method on a value as long as the value is
addressable. In other words, you don't need to have a value receiver version
of the method in some cases.

Not every variable is addressable though. Map elements are not addressable."

Since everything in memory has an address I am confused by these statements.
Does the author mean that "not everything can be pointed to" with a pointer
variable in Go?

~~~
bumbledraven
Go itself says you "cannot take the address" of some values. For example:

    
    
      m := map[int]int{1: 2}
      p := &m[1]
    

The second line causes the compilation error "cannot take the address of
m[1]".

[https://play.golang.org/p/pIMgeQzOSZK](https://play.golang.org/p/pIMgeQzOSZK)

~~~
bogomipz
Thanks, I think it was the wording "addressable" that was the source of my
confusion. "cannot take the address of" seems more intuitive to me. Cheers.

------
vsenko
It would be great if you could elaborate on this: "Sending to an Unbuffered
Channel Returns As Soon As the Target Receiver Is Ready"

As far as I understand, the problem is that the printing goroutine is forced
to exit when the main goroutine exits. This way it's unknown if all the
submitted work was done.

It seems to me that the caption is a bit misleading, it implies that the
problem occurs only with "Unbuffered" channels.

------
smg
I am not sure why

* Unused Variables

* Unused Imports

are being considered traps or gotchas. They are in fact very sensible design
decisions for a compiled language. Having a compile-time error because of
mistyping a variable is far better than having to debug a runtime failure.

