Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Cuneytt/cbf63b6b809cf53d0d3bca77bbaa30b2 to your computer and use it in GitHub Desktop.

Select an option

Save Cuneytt/cbf63b6b809cf53d0d3bca77bbaa30b2 to your computer and use it in GitHub Desktop.

Revisions

  1. @lavalamp lavalamp renamed this gist Feb 28, 2015. 1 changed file with 0 additions and 0 deletions.
  2. @lavalamp lavalamp revised this gist Feb 28, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion The Three Go Landmines
    Original file line number Diff line number Diff line change
    @@ -81,4 +81,4 @@ func DoTheThing(reallyDoIt bool) (err error) {
    }
    ```

    The above function will *always* return a nil error, because the inner err variable "shadows" the function scope variable. Adding a ```var result string``` and not using ```:=``` would fix this.
    The above function will *always* return a nil error, because the inner err variable "shadows" the function scope variable. Adding a ```var result string``` and not using ```:=``` would fix this.
  3. @lavalamp lavalamp revised this gist Feb 28, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion The Three Go Landmines
    Original file line number Diff line number Diff line change
    @@ -81,4 +81,4 @@ func DoTheThing(reallyDoIt bool) (err error) {
    }
    ```

    The above function will *always* return a nil error, because the inner err variable "shadows" the function scope variable. Adding a ```var result string``` and not using ```:=``` would fix this.
    The above function will *always* return a nil error, because the inner err variable "shadows" the function scope variable. Adding a ```var result string``` and not using ```:=``` would fix this.
  4. @lavalamp lavalamp created this gist Feb 28, 2015.
    84 changes: 84 additions & 0 deletions The Three Go Landmines
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    There are three easy to make mistakes in go. I present them here in the way they are often found in the wild, not in the way that is easiest to understand.

    All three of these mistakes have been made in Kubernetes code, getting past code review at least once each that I know of.

    1. Loop variables are scoped outside the loop.

    What do these lines do? Make predictions and then scroll down.

    ```go
    func print(pi *int) { fmt.Println(*pi) }

    for i := 0; i < 10; i++ {
    defer fmt.Println(i)
    defer func(){ fmt.Println(i) }()
    defer func(i int){ fmt.Println(i) }(i)
    defer print(&i)
    go fmt.Println(i)
    go func(){ fmt.Println(i) }()
    }
    ```

    Answers:

    ```go
    func print(pi *int) { fmt.Println(*pi) }

    for i := 0; i < 10; i++ {
    defer fmt.Println(i) // OK; prints 9 ... 0
    defer func(){ fmt.Println(i) }() // WRONG; prints "10" 10 times
    defer func(i int){ fmt.Println(i) }(i) // OK
    defer print(&i) // WRONG; prints "10" 10 times
    go fmt.Println(i) // OK; prints 0 ... 9 in unpredictable order
    go func(){ fmt.Println(i) }() // WRONG; totally unpredictable.
    }

    for key, value := range myMap {
    // Same for key & value as i!
    }
    ```

    Everyone expects these values to be scoped inside the loop, but go reuses the same memory location for every iteration. This means that you must never let the address of ```key```, ```value```, or ```i``` above escape the loop. An anonymous ```func() { /* do something with i */ }``` (a "closure") is a subtle way of taking a variable's address

    2. Nil interface is not the same as having a nil pointer in the interface.

    ```go
    type Cat interface {
    Meow()
    }

    type Tabby struct {}
    func (*Tabby) Meow() { fmt.Println("meow") }

    func GetACat() Cat {
    var myTabby *Tabby = nil
    // Oops, we forgot to set myTabby to a real value
    return myTabby
    }

    func TestGetACat(t *testing.T) {
    if GetACat() == nil {
    t.Errorf("Forgot to return a real cat!")
    }
    }
    ```

    Guess what? The above test will NOT detect the nil pointer! This is because the interface serves as a pointer, so GetACat effectively returns a pointer to a nil pointer. Never do the thing that GetACat does, and you (and your coworkers) will be much happier. It is *very* easy to do this with error values. http://golang.org/doc/faq#nil_error

    3. Shadowing considered harmful to your sanity.

    ```go
    var ErrDidNotWork = errors.New("did not work")

    func DoTheThing(reallyDoIt bool) (err error) {
    if reallyDoIt {
    result, err := tryTheThing()
    if err != nil || result != "it worked" {
    err = ErrDidNotWork
    }
    }
    return err
    }
    ```

    The above function will *always* return a nil error, because the inner err variable "shadows" the function scope variable. Adding a ```var result string``` and not using ```:=``` would fix this.