Go tip of the week: Communicate by (not) sharing memory
Pop quiz: Does the following code share memory by communicating or communicate by sharing memory?
func Map[T any, R any](collection []T,
iter func(item T, index int) R) []R {
result := make([]R, len(collection))
var wg sync.WaitGroup
wg.Add(len(collection))
for i, item := range collection {
go func(item T, i int) {
res := iter(item, i)
result[i] = res
wg.Done()
}(item, i)
}
wg.Wait()
return result
}
Basically, the code iterates over a slice of T
s and spawns a goroutine for each item. All goroutines write their result into the same result slice, concurrently, without mutexes.
Is the result slice shared memory? After all, it's a single, contiguous data structure.
On the other hand, each goroutine only writes into the index it has been assigned to, so concurrent writing into the same location cannot happen. And the result slice is not read until all writing goroutines have finished.
So even though the goroutines write into the same slice, they seem to adhere to the Go proverb that says, “Don't communicate by sharing memory, share memory by communicating.”
What do you think?