2026-10-09

#golang

What will be printed?

package main

import (
  "fmt"
)

func main() {
  foo := 1
  defer fmt.Println(foo)
  Up(&foo)
  defer fmt.Println(foo)
}

func Up(bar *int) {
  *bar = *bar + 1
}

Answer:
2
1

And now - wrapped #defer with a func:

package main

import (
  "fmt"
)

func main() {
  foo := 1
  defer func() {
    fmt.Println(foo)
  }()
  Up(&foo)
  defer func() {
    fmt.Println(foo)
  }()
}

func Up(bar *int) {
  *bar = *bar + 1
}

Answer:
2
2

Это фанатский мод: от первого лица с оружием CS ходишь по картам и кампаниям Warcraft III, мир и квесты остаются варкрафтовскими.

Нужны Warcraft III 1.26a и файлы CS 1.6.

Репозиторий: https://github.com/YazgulDev/warcraft-cs

Видео автора мода: https://www.youtube.com/watch?v=hW0TytuwpRA

#golang
#race

package main

import "fmt"

func main() {
  var done bool
  var msg string

  go func() {
    msg = "hello"
    done = true
  }()

  for !done {
  }
  fmt.Println(msg)
}

Can you imagine - sometimes will be printed an empty string.

-race reports data race here.

#gemini 3.8 flash high explanation:

Goroutines do not have separate memory blocks; all goroutines share the same virtual address space.

An empty string can still be printed, but that happens because of compiler/hardware reordering and cache visibility, caused by a data race.

  ### Why this happens

  1. Shared Address Space:
  Both variables (done and msg) reside in shared memory. Goroutines are lightweight threads managed by the Go runtime, not isolated processes.

  2. No "Happens-Before" Guarantee:
  According to the Go memory model, reads and writes across goroutines are only ordered when synchronized through mechanisms like channels, mutexes, or atomic operations. Without synchronization, the Go compiler and the CPU are free to:
      • Reorder the writes msg = "hello" and done = true.
      • Reorder the reads in main.
      • Keep msg or done in CPU registers or store buffers without flushing to shared cache.
  If done = true becomes visible to main before msg = "hello" does, fmt.Println(msg) prints "".

  3. Infinite Loop Risk:
  The code might also never terminate. The compiler can observe that done is not modified inside the body of for !done {} in main and hoist the read or keep done in a register:

    // What the compiler is allowed to generate:
    if !done {
        for {
        }
    }


  ### Detection

  You can detect this issue using the Go race detector:

    go run -race main.go

  ### Proper Solution

  Use synchronization tools such as channels, sync.WaitGroup, or atomic values to establish a happens-before relationship:


    package main

    import (
       "fmt"
      "sync"
    )

    // main coordinates goroutines using sync.WaitGroup to guarantee visibility.
    func main() {
        var (
            wg sync.WaitGroup
            msg string
        )

        wg.Add(1)
        go func() {
            defer wg.Done()
            msg = "hello"
        }()

        wg.Wait()
        fmt.Println(msg)
    }