Don't do this!
Stupid things you can do with golang
Mihail Mikov
25.03.2025
Mihail Mikov
25.03.2025
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() }
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!") }
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 } } }
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) } }
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.
8This 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) } } }
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 } } } }
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]) }
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)) }
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() }
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() }
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) }
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) }
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 // 🔒 }
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
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) }
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{}) }
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}) }
Mihail Mikov
25.03.2025