Go 1.25+ added a new method to the sync package that removes common boilerplate: the WaitGroup.Go() method. If you've used wg.Add(1), go func(), and defer wg.Done() hundreds of times, this one's for you.
The old way
Here's how we launch concurrent goroutines:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
list1 := []int{10, 20, 30, 40}
list2 := []int{1, 10, 20, 30, 40}
l1 := []int{-1, 4, 5, 6}
l2 := []int{4, 5, 6}
wg.Add(1)
go func() {
defer wg.Done()
solution(list2, list1)
}()
wg.Add(1)
go func() {
defer wg.Done()
solution(l2, l1)
}()
wg.Wait()
}
func solution(x, y []int) {
var uniqueIdx int
for _, num1 := range x {
uniqueIdx ^= num1
}
for _, num2 := range y {
uniqueIdx ^= num2
}
fmt.Println(uniqueIdx)
}
This works fine. But look at the steps: increment the counter manually, wrap everything in an anonymous function, remember to defer the done call. For every single goroutine.
The new way
Now we can write:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
list1 := []int{10, 20, 30, 40}
list2 := []int{1, 10, 20, 30, 40}
l1 := []int{-1, 4, 5, 6}
l2 := []int{4, 5, 6}
wg.Go(func() {
solution(list1, list2)
})
wg.Go(func() {
solution(l2, l1)
})
wg.Wait()
}
func solution(x, y []int) {
var uniqueIdx int
for _, num1 := range x {
uniqueIdx ^= num1
}
for _, num2 := range y {
uniqueIdx ^= num2
}
fmt.Println(uniqueIdx)
}
The Go() method handles the counter increment and the Done() call automatically. Three steps become one clean method call.
What it does
The implementation is simple:
// https://github.com/golang/go/blob/master/src/sync/waitgroup.go
func (wg *WaitGroup) Go(f func()) {
wg.Add(1)
go func() {
defer wg.Done()
f()
}()
}
It's exactly what we've been writing manually, just packaged properly.
If you're on Go 1.25 or later, start using wg.Go() where it makes sense.
Thanks to Anton Zhiyanov for his excellent Gist of Go: Concurrency guide.
Top comments (0)