If no writes are involved, concurrent reads are always safe, regardless of the data structure. However, as soon as even a single concurrency-unsafe write to a variable is involved, you need to serialise concurrent access (both writes and reads) to the variable.
Moreover, you can safely write to elements of a slice or go error an array under the condition that no more than one goroutine write to a given element.
For instance, if you run the following programme with the race detector on, it's likely to report a race condition, because multiple goroutines concurrently modify variable results
without precautions:
package main import ( "fmt" "sync" ) func main() { const n = 8 var results []int var wg sync.WaitGroup wg.Add(n) for i := 0; i < n; i++ { i := i go func() { defer wg.Done() results = append(results, square(i)) }() } wg.Wait() fmt.Println(results) } func square(i int) int { return i * i }
However, the following programme contains no such no synchronization bug, because each element of the slice is modified by a single goroutine:
package main import ( "fmt" "sync" ) func main() { const n = 8 results := make([]int, n) var wg sync.WaitGroup wg.Add(n) for i := 0; i < n; i++ { i := i go func() { defer wg.Done() results[i] = square(i) }() } wg.Wait() fmt.Println(results) } func square(i int) int { return i * i }