Must know questions about Golang

Update from range

Note

The data values generated in the range clause are copies of the actual collection elements. They are not references to the original items. This means that updating the values will not change the original data. It also means that taking the address of the values will not give you pointers to the original data.

package main

import "fmt"

func bad() {
	data := []int{1, 2, 3}
	for _, v := range data {
		v *= 10 //original item is not changed
	}

	fmt.Println("data:", data) //prints data: [1 2 3]
}

func good() {
	data := []int{1, 2, 3}

	for i := range data {
		data[i] *= 10
	}

	fmt.Println("data:", data) //prints data: [10 20 30]
}

// If your collection holds pointer values then the rules are slightly different.
// You still need to use the index operator if you want the original record to point
// to another value, but you can update the data stored at the target location using
//  the second value in the "for range" clause.
func updateData() {
	data := []*struct{ num int }{{1}, {2}, {3}}

	for _, v := range data {
		v.num *= 10
	}
	fmt.Println(data[0], data[1], data[2]) //prints &{10} &{20} &{30}
}

func main() {
	fmt.Println("Bad:")
	bad()
	fmt.Println("Good:")
	good()
	fmt.Println("Update pointer collection data:")
	updateData()
}

Iteration Variables and Closures in for Statements

Note

This is the most common gotcha in Go. The iteration variables in for statements are reused in each iteration. This means that each closure (aka function literal) created in your for loop will reference the same variable (and they’ll get that variable’s value at the time those goroutines start executing).

// Iteration Variables and Closures in "for" Statements

package main

import (
	"fmt"
	"time"
)

// This is the most common gotcha in Go. The iteration variables in for statements
// are reused in each iteration. This means that each closure (aka function literal)
// created in your for loop will reference the same variable (and they'll get that
// variable's value at the time those goroutines start executing).
func incorrect() {
	data := []string{"one", "two", "three"}

	for _, v := range data {
		go func() {
			fmt.Println(v)
		}()
	}

	time.Sleep(time.Second * 3)
	// goroutines print: three, three, three
}

// The easiest solution (that doesn't require any changes to the goroutine) is to
// save the current iteration variable value in a local variable inside the for loop block.
func work1() {
	data := []string{"one", "two", "three"}

	for _, v := range data {
		vcopy := v // copey v to local var inside for loop
		go func() {
			fmt.Println(vcopy)
		}()
	}

	time.Sleep(3 * time.Second)
}

// Another solution is to pass the current iteration variable as a parameter to
// the anonymous goroutine.
func work2() {
	data := []string{"one", "two", "three"}

	for _, v := range data {
		go func(in string) {
			fmt.Println(in)
		}(v)
	}

	time.Sleep(3 * time.Second)
}

type field struct {
	name string
}

func (f *field) print() {
	fmt.Println(f.name)
}

func incorrect2() {
	data := []field{{"one"}, {"two"}, {"three"}}

	for _, v := range data {
		go v.print()
	}

	time.Sleep(3 * time.Second)
}

func fixIncorrect2() {
	data := []field{{"one"}, {"two"}, {"three"}}

	for _, v := range data {
		v := v
		go v.print()
	}

	time.Sleep(3 * time.Second)
}

func guess() {
	data := []*field{{"one"}, {"two"}, {"three"}}

	for _, v := range data {
		go v.print()
	}

	time.Sleep(3 * time.Second)
}

func main() {
	fmt.Println("This is the incorrect:")
	incorrect()
	fmt.Println("This is the ok 1:")
	work1()
	fmt.Println("This is the ok 2:")
	work2()
	fmt.Println("This is the incorrect2:")
	incorrect2()
	fmt.Println("This is the fixIncorrect2:")
	fixIncorrect2()
	guess()
}