主页

索引

模块索引

搜索页面

3.10.4. 变量疑难杂症

惰性求值和闭包

实例:

a := []int{1,2,3}
for _, i:= range a {
  go func() {
    fmt.Println(i)
  }()
}
time.Sleep(time.Second)
// Output:
// 3
// 3
// 3

检索关键字:

glang faq loop closure
闭包的特性
可以通过 go vet命令来检查程序中类似的问题

两种解决方法。其中第一种方法很好理解,就是把变量v的值通过参数的形式传递给goroutine,因为go中func的参数传递都是值传递,所以就在goroutine启动时获得了当前v变量的值:

for _, v := range values {
    go func(u string) {
        fmt.Println(u)
        done <- true
    }(v)
}

第二种方法是更巧妙地通过一个赋值语句来解决的:

for _, v := range values {
    v := v // create a new 'v'.
    go func() {
        fmt.Println(v)
    }()
}

备注

In Go, the loop iterator variable is a single variable that takes different values in each loop iteration. This is very efficient, but might lead to unintended behavior when used incorrectly.

同样类似 loop iteration variable 的问题还有这种:

// Using reference to loop iterator variable
// Due to efficiency, the loop iterator variable is
    // a single variable that takes different values in each loop iteration.
in := []int{1, 2, 3}

var out []*int
for  _, v := range in {
    out = append(out, &v)
}

fmt.Println("Values:", *out[0], *out[1], *out[2])
fmt.Println("Addresses:", out[0], out[1], out[2])

output:
// Values: 3 3 3
// Addresses: 0xc000014188 0xc000014188 0xc000014188

原因:
    range 中的 value 使用相同的指针地址
解决方法:
for  _, v := range in {
    v = v // create a new 'v'.
    out = append(out, &v)
}

备注

This behavior of the language, not defining a new variable for each iteration, may have been a mistake in retrospect. It may be addressed in a later version but, for compatibility, cannot change in Go version 1.

关于 single variable 的说明:

a := 123
fmt.Printf("%v\n", a)
fmt.Printf("%v\n", &a)
a = 456
fmt.Printf("%v\n", a)
fmt.Printf("%v\n", &a)

// 打印:
123
0xc000018050
456
0xc000018050

指针与切片实例

以下代码输出什么:

s:="123"
ps:=&s
b:=[]byte(s)
pb:=&b

s+="4"
*ps+="5"
(*pb)[1] = 0
(*pb)[2] = 4

fmt.Printf("%+v\n",*ps)
fmt.Printf("%+v\n",*pb)

答案:

"12345"[49,0,4]

关键在于:

b:=[]byte(s)

说明:

A string contains an array of bytes that, once created, is immutable.
By contrast, the elements of a byte slice can be freely modified.
Strings can be converted to byte slices and back again:

s := “abc”
b := []byte(s)
s2 := string(b)

Conceptually, the []byte(s) conversion allocates a new byte array
    holding a copy of the bytes of s,
    and yields a slice that references the entirety of that array.
An optimizing compiler may be able to avoid the allocation and copying in some cases,
    but in general copying is required to ensure that the bytes of s remain unchanged
    even if those of b are subsequently modified.
The conversion from byte slice back to string with string(b) also makes a copy,
    to ensure immutability of the resulting string s2.

主页

索引

模块索引

搜索页面