Don't do this!

Stupid things you can do with golang

Mihail Mikov

25.03.2025

defer

Deffered functions are executed in reverse order. Also everything after the ; isn't actually deffered

package main

import "fmt"

func a(from string) {
	fmt.Println(from, ": a")
}

func b(from string) {
	fmt.Println(from, ": b")
}

func c(from string) {
	fmt.Println(from, ": c")
}

func x1() {
    defer a("x1")
    defer b("x1")
    c("x1")
}

func x2() {
    defer a("x2"); b("x2")
    c("x2")
}

func main() {
    x1()
    x2()
}
2

abusing panic/recover

package main

import "fmt"

type Mountain struct{}

func (m *Mountain) Climb() {
	fmt.Println("climbing")
}

func ClimbAllPanicRecover(mountains []Mountain) {
    defer func() {
        fmt.Println("ops, went too high...")
        recover()
    }()
    for i := 0; ; i++ {
        mountains[i].Climb() // panics when i == len(mountains)
    }
    fmt.Println("unreachable, even after recover")
}

func main() {
    mountains := make([]Mountain, 5)
    ClimbAllPanicRecover(mountains)
    fmt.Println("at the peak!")
}
3

4

leaky sentinel errors

Sentinel errors aren't bad, but can cause leaky abstractions

package main

import (
	"errors"
	"fmt"
)

// imagine importing this from a deep dependency
var myError = errors.New("something went wrong")

func try() error {
    // imagine calling a deep stack of functions to get to
    return fmt.Errorf("try: %w", &myError)
}

func doIt() {
    err := try()
    if err != nil {
        if errors.Is(err, myError) {
            // we now know the type of the error
        }
    }
}
5

6

special errors

This is a great pattern in theory. Unfortunately, go insists on making it harder...

package main

import (
	"context"
	"fmt"
	"sync"
)

type (
	reportable interface {
		Report(ctx context.Context)
	}

	retryable interface {
		CanRetry() bool
	}

	mySpecialError struct {
		once               sync.Once
		retries            int
		importantDebugInfo string
	}
)

func (e *mySpecialError) Error() string {
	return e.importantDebugInfo
}

func (e *mySpecialError) Report(ctx context.Context) {
	e.once.Do(func() {
		fmt.Println(e.Error())
	})
}

func (e *mySpecialError) CanRetry() bool {
	return e.retries > 0
}

func DoSomethingThatMightFail(b bool) error {
	if b {
		return nil
	}

	return &mySpecialError{
		retries:            1,
		importantDebugInfo: "test stuff",
	}
}

func main() {
    err := DoSomethingThatMightFail(false)
    switch e := err.(type) {
    case reportable:
        e.Report(context.TODO())
        fallthrough
    case retryable:
        if e.CanRetry() {
            fmt.Println("try again!")
        }
    case error:
        fmt.Println("any other type of error")
    default:
        fmt.Println("nil?", e)
    }
}
7

functional go

Adopting a functional style in go is possible, but is it desirable?

RunPipe(3*time.Second, func(ctx context.Context, _ func()) {
  urls := FromFile(ctx, "user_urls.txt")
  bodies := Parallel(ctx, urls, 10, request)
  parsed := Parallel(ctx, bodies, 3, parseJSON)
  admins := Filter(ctx, parsed, isAdmin)
  entries := Map(ctx, admins, toJSON)
  err := ToFile(ctx, entries, "admins.txt")
    if err != nil {
      return err
    }
})

Short answer: no.

The operators variety would get so broad reading any code would require an immense amount of knowledge.

8

9

nested iterators

This doesn't look so bad, right?

package main

import (
	"fmt"
	"iter"
)

// Iterator that returns numbers from start to end
func rangeIterator(start, end int) iter.Seq[int] {
	return func(yield func(int) bool) {
		for i := start; i <= end; i++ {
			if !yield(i) {
				return
			}
		}
	}
}

// Iterator that returns other iterators
func nestedIterator(start, end, size int) iter.Seq[iter.Seq[int]] {
	return func(yield func(iter.Seq[int]) bool) {
		for i := start; i <= end; i += size {
			nestedEnd := i + size - 1
			if nestedEnd > end {
				nestedEnd = end
			}

			// rangeIterator is another, similar iterator func
			subRange := rangeIterator(i, nestedEnd) // HLxxx

			if !yield(subRange) {
				return
			}
		}
	}
}

func main() {
    chunkedRanges := nestedIterator(1, 10, 3)

    for chunk := range chunkedRanges {
        fmt.Println("New chunk:")

        // Iterate through the inner iterator
        for num := range chunk {
            fmt.Printf("  %d\n", num)
        }
    }
}
10

nested iterators: implementation

until you look under the hood 😱

func nestedIterator(start, end, size int) iter.Seq[iter.Seq[int]] {
    return func(yield func(iter.Seq[int]) bool) {
        for i := start; i <= end; i += size {
            nestedEnd := i + size - 1
            if nestedEnd > end {
                nestedEnd = end
            }

            // rangeIterator is another, similar iterator func
            subRange := rangeIterator(i, nestedEnd)

            if !yield(subRange) {
                return
            }
        }
    }
}
11

12

when all you know is a 🔨

not verbatim, but I've seen a lot of similar slice walking to get to a value

package main

import "fmt"

var myMap = map[string]string{
	"test":   "123",
	"wanted": "got it fast!",
	"other":  "xxx",
}

func main() {
    keys := make([]string, 0, len(myMap))
    values := make([]string, 0, len(myMap))

    for k, v := range myMap {
        keys = append(keys, k)
        values = append(values, v)
    }

    var wantedIndex int
    for i, k := range keys {
        if k == "wanted" {
            wantedIndex = i
            break
        }
    }

    fmt.Println("value:", values[wantedIndex])
}
13

14

floats

As with any language that uses the IEEE 754, floats are "fun"

package main

import "fmt"

func smartSumEqual(u, v, w float64) bool {
    return (u+v == w) || (u == w-v) || (v == w-u)
}

func actualSolution(u, v, w float64) bool {
    return u+v-w < 1e-8
}

func main() {
    x, y, z := 0.1, 0.2, 0.3

    fmt.Println("0.1 + 0.2 == 0.3 ?", x+y == z)
    fmt.Println("'smart' sum equals ?", smartSumEqual(x, y, z))
    fmt.Println("actual sum equals ?", actualSolution(x, y, z))
}
15

callable structs?!

Just calling some methods on a struct, nothing to see here.. wait, wat?!

package main

import "fmt"

type ImAStructTrustMe func()

func (ImAStructTrustMe) DoStuff() {
	fmt.Println("doing")
}

func (ImAStructTrustMe) AnotherMethod() {
	fmt.Println("much lol")
}

var myStruct ImAStructTrustMe = func() {
	fmt.Println("WTF?!")
}

func main() {
    myStruct.DoStuff()
    myStruct.AnotherMethod()

    // WTF!?
    myStruct()
}
16

funcs with methods

Who would do this? (any why?!)

package main

import "fmt"

type ImAStructTrustMe func()

func (ImAStructTrustMe) DoStuff() {
    fmt.Println("doing")
}

func (ImAStructTrustMe) AnotherMethod() {
    fmt.Println("much lol")
}

var myStruct ImAStructTrustMe = func() {
    fmt.Println("WTF?!")
}

func main() {
    myStruct.DoStuff()
    myStruct.AnotherMethod()

    // WTF!?
    myStruct()
}
17

nil by default

It's pretty easy to forget to initialize a map, slice or chan

package main

func main() {
    var myMap map[string]string

    myMap["key"] = "value"
}

Especially since other values are initialized by default

package main

import "fmt"

type aStruct struct {
    anInt   int
    aString string
}

func main() {
    var s aStruct

    fmt.Println(s)
}
18

19

easy race

This should return 200 every time, right? right?!

package main

import (
	"fmt"
	"sync"
)

type SharedInt struct {
	mu  sync.Mutex
	val int
}

func (si *SharedInt) Val() int {
	si.mu.Lock()
	defer si.mu.Unlock() // 🔒
	return si.val        // 🔒
}

func (si *SharedInt) SetVal(val int) {
	si.mu.Lock()
	defer si.mu.Unlock() // 🔒
	si.val = val         // 🔒
}

func main() {
    myInt := &SharedInt{}

    wg := sync.WaitGroup{}
    wg.Add(200)

    for i := 0; i < 200; i += 1 {
        go func() { myInt.SetVal(myInt.Val() + 1); wg.Done() }()
    }

    wg.Wait()
    fmt.Println(myInt.val)
}
20

easy race: internals

This seems correct...

type SharedInt struct {
    mu  sync.Mutex
    val int
}

func (si *SharedInt) Val() int {
    si.mu.Lock()
    defer si.mu.Unlock() // 🔒
    return si.val        // 🔒
}

func (si *SharedInt) SetVal(val int) {
    si.mu.Lock()
    defer si.mu.Unlock() // 🔒
    si.val = val         // 🔒
}
21

easy race: what is actually going on

While this pattern is common in other languages (hi, Java). It doesn't really work in go

v := myInt.Val()
v += 1 // 😱😱😱
myInt.SetVal(v)
22

23

slices

Slices are pointers to the underlying data

package main

import "fmt"

func main() {
    a := []int{1, 2, 3, 4}
    b := a[:2] /* [1, 2] */
    c := a[2:] /* [3, 4] */

    b = append(b, 5)

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
24

structural typing for structs

Structural typing seems a bit inconsistent...

package main

type (
    x struct{ s string }
    y struct{ s string }   // try adding another field
    z = struct{ s string } // type alias
)

func f(p struct{ s string }) {}
func g(p x)                  {}
func d(p z)                  {}

func do() {
    f(x{})
    f(y{})

    g(x{})
    g(y{}) // error: cannot use y as x

    d(x{})
    d(y{})
}
25

union types?!

No, this is just a way to define type constraints for generics

package main

import "fmt"

type point struct{ x, y float64 }

type union interface {
    string | int | bool | point
}

func use[T union](p T) {
    fmt.Println(p)
}

func main() {
    use("what?")
    use(5) // change to 5.1
    use(true)
    use(point{x: 3.14, y: 2.71})
}
26

Useful links & sources

27

28

Thank you!

Mihail Mikov

25.03.2025