Gopher Puzzlers

How well do you know your language?

12 December 2019

Dave Cheney

Who's read this book?

2

Gopher Puzzlers

Hopefully this little quiz will surprise you, and perhaps teach you something you didn't know about Go.

3

10th Birthday Aniversary Edition

Here are the rules

4

Main 🤷‍♀️ Main

Consider this program

package main

import "fmt"

var x int

func init() {
    main()
}

func main() {
    x++
    fmt.Println(x)
}

Credit: Damian Gryski

What does this program print?

5

Counting

Consider this program

package main

import (
    "fmt"
)

func main() {
    i := []int{
        1, 2, 3,
    }
    for j := range i {
        fmt.Println(j)
    }
}

What does this program print?

6

Sliced to ribbons

Consider this program

package main

import (
    "fmt"
)

func main() {
    s := make([]int, 3, 8)
    s[2:5][0] = 5
    fmt.Println(s)
}

What does this program print?

7

Truth or dare

Consider this program

package main

import "fmt"

func main() {
    false := true
    fmt.Println(true == false == true)
}

What does this program print?

8

Truth of dare (cont.)

Consider this program

package main

import (
    "fmt"
)

func main() {
    var err error
    _, true := err.(error)
    fmt.Println(true)
}

What does this program print?

9

Truth or dare (cont.)

true and false are not keywords, they are predeclared identifiers.

true and false are declared at the universe block, a block that encompases all Go source.

10

Truth or dare (cont.)

Consider this program

package main

import "fmt"

func main() {
    true := 6
    fmt.Println(true > false)
}

What does this program print?

11

Pop a cap

Consider this program

package main

import "fmt"

func main() {
    s := make([]int, 0, 8)
    fmt.Println(len(s))
}

What does this program print?

12

Pop a cap (cont.)

Consider this program

package main

import "fmt"

func main() {
    s := make([]int, 0, 8)
    r := s[2:6:7]
    fmt.Println(len(r), cap(r))
}

What does this program print?

13

Pop a cap (cont.)

s is a []int slice.

The length of s is 0, the capacity of s is 8.

Any slice can be resliced to make it smaller and larger, up to its capacity.

However, the three arg slice operation permits the caller to specify the maximum capacity of a slice.

Thus r is a []int slice of length 4 and capacity 5.

14

Gophers are complex

Consider this program

package main

import "fmt"

var i = 2

func main() {
    j := 2 * 2i
    fmt.Println(j)
}

What does this program print?

15

Why, iota

Consider this program

package main

import (
    "fmt"
)

const (
    c1 int = iota
    c2
    c3
    c4 rune = iota
    c5
)

func main() {
    fmt.Println(c5)
}

What does this program print?

16

Touché

Consider this program

package main

import "fmt"

func main() {
    s := string("touché")
    b := []byte{'t', 'o', 'u', 'c', 'h', 'é'}
    fmt.Println(len(s) == len(b))
}
17

Maps

Does this program compile?

package main

import "fmt"

func main() {
    m := make(map[string]int)
    m["foo"]++
    fmt.Println(m["foo"])
}

If it does, what does it print?

18

Maps (solution)

Yes, it does compile.

Here is the same program rewritten to be more explicit.

package main

import "fmt"

func main() {
    m := make(map[string]int)
    v := m["foo"]
    v++
    m["foo"] = v
    fmt.Println(m["foo"])
}
19

Animal, mineral, vegetable

Consider this program

package main

import "fmt"

const (
    Animal = iota
    Mineral
    Vegetable
)

func genus(t uint) {
    switch t {
    case Animal:
        fmt.Println("Animal")
    case Mineral:
        fmt.Println("Mineral")
    case Vegetable:
        fmt.Println("Vegetable")
    }
}

func main() {
    genus(-Animal)
}
20

Evaluation order oddity

Consider this program

package main

import "fmt"

var x = 1

func a() int {
    x *= 2
    return x
}

func b() int {
    x /= 2
    return x
}

func sub(a, b int) int {
    return a - b
}

func main() {
    fmt.Println(sub(a(), b()))
}

What does it print?

21

Tick, Tick, Tick

Consider this program

package main

import "time"

func main() {
    t := time.NewTicker(100)
    for range t.C {
        t.Stop()
    }
}

Does it

22

Tick, Tick, Tick (solution)

Stop turns off a ticker. After Stop, no more ticks will be sent. Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.

package main

import (
	"fmt"
	"time"
)

func main() {
    t := time.NewTicker(100)
    t.Stop()
    for {
        select {
        case <-t.C:
            fmt.Println("tick")
        }
    }
}
23

Floaty McFloatface

Consider this program

package main

import "fmt"

func main() {
    var x uint32 = 100.5 + 100.5 + 2147483447.0
    fmt.Println(x)
}

Does it compile?

24

Floaty McFloatface (solution)

Although Go does not permit implicit conversion between variables, the rules are different for constants.

Specifically:

A constant value x can be converted to type T in any of these cases:

25

Snip, Snip

What does this program print?

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := strings.TrimRight("abcdefedcba", "abcdef")
    fmt.Printf("%q\n", s)
}
26

Snip, Snip (solution)

strings.TrimRight takes a cutset for their second argument.

If the rightmost character is a member of the cutset, it is removed, and we go again, otherwise stop.

strings.Trim{,Left,Right} are great for trimming a set of characters (usually whitespace), from a string, but if you know the prefix or suffix, you probably want strings.Trim{Prefix,Suffix}

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := strings.TrimSuffix("abcdefedcba", "abcdef")
    fmt.Printf("%q\n", s)
}
27

The Terminator

Will this program terminate?

package main

import "fmt"

func main() {
    v := []int{1, 2, 3}
    for i := range v {
        v = append(v, i)
    }
    fmt.Println(v)
}
28

The Terminator (solution)

The for ... range syntax only evaluates the terminating condition once, at the start of the loop.

package main

import "fmt"

func main() {
    v := []int{1, 2, 3}
    for i, n := 0, len(v); i < n; i++ {
        v = append(v, i)
    }
    fmt.Println(v)
}

Take Away: if you are modifying a slice while iterating over it; use a regular for loop.

29

Do you want to print a Snowman?

What does this code print

package main

import "fmt"

func main() {
    r := []rune("☃")
    fmt.Println(r)
}
30

Do you want to print a Snowman? (solution)

rune is an alias for int32.

package main

import (
    "fmt"
)

func main() {
    var r rune
    fmt.Printf("%T\n", r)
}

What does this program print?

Question: How many other predeclared aliases are there? What are their names?

31

A slice of aliases

Which of these declarations are valid?

package main

import "fmt"

func main() {
    b := []byte("Hello")
    r := []rune("Sydney")
    i := []int("Gophers")
    fmt.Println(b, r, i)
}
32

Pretty vacant

Consider this program

package main

import "fmt"

func main() {
    fmt.Println(len(map[interface{}]int{
        new(int):      1,
        new(int):      2,
        new(struct{}): 3,
        new(struct{}): 4,
    }))
}

Credit: Brad Fitzpatrick

33

Pretty vacant (cont.)

Spec: Size and alignment guarantees:

"A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory."

package main

import "fmt"

func main() {
    p, q := new(int), new(int)
    fmt.Println(*p == *q, p == q)

    r, s := new(struct{}), new(struct{})
    fmt.Println(*r == *s, r == s)
}
34

Pretty vacant (cont.)

package main

import "fmt"

var (
    p, q = new(int), new(int)
    r, s = new(struct{}), new(struct{})
)

func main() {
    fmt.Println(*p == *q, p == q)
    fmt.Println(*r == *s, r == s)
}
35

Tokens

Does this code compile?

package main

type bar0 struct {
    n *
        bar0
    m []bar0
}

func main() {
}

Credit: fuuucl.am on golang-dev.

36

Tokens (cont.)

Yes, it does compile.

If it did not, you would not be able to write

package main

import "fmt"

func main() {
    x := 1 *
        2 *
        3
    fmt.Println(x)
}

When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line's final token if that token is

37

Thank you

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)