我相信很多人一看就看出问题在哪了,但肯定还有人不清楚 for range 语法的机制,我絮叨一下:golang中 for range 语法非常方便,可以轻松的遍历 array 、 slice 、 map 等结构,但是它有一个特点,就是会在遍历时把当前遍历到的元素,复制给内部变量,具体就是在 whoIsDead 函数中的 for range 里,会把 people 里的每个 person ,都复制给 p 这个变量,类似于这样的操作:
p := person
上文说过, struct 是值类型,所以在赋值给 p 的过程中,实际上需要重新生成一份 person 数据,便于 for range 内部使用,不信试试:
package main
import "fmt"
type person struct {
name string
age byte
isDead bool
}
func main() {
p1 := person{name: "zzy", age: 100}
p2 := p1
p1.name = "changed"
fmt.Println(p2.name)
}
所以 p.isDead = true 这个操作实际上更改的是新生成的 p 数据,而非 people 中原本的 person ,这里产生了一个bug。
在 for range 内部只需读取数据而不需要修改的情况下,随便怎么写也无所谓,顶多就是代码不够完美,而需要修改数据时,则最好传递 struct 指针:
package main
import "fmt"
type person struct {
name string
age byte
isDead bool
}
func main() {
p1 := &person{name: "zzy", age: 100}
p2 := &person{name: "dj", age: 99}
p3 := &person{name: "px", age: 20}
people := []*person{p1, p2, p3}
whoIsDead(people)
for _, p := range people {
if p.isDead {
fmt.Println("who is dead?", p.name)
}
}
}
func whoIsDead(people []*person) {
for _, p := range people {
if p.age < 50 {
p.isDead = true
}
}
}
运行一下:
who is dead? px
everything is ok,很棒棒的代码。
还有另外的方法,使用索引访问 people 中的 person ,改动一下 whoIsDead 函数,也能达到同样的目的:
func whoIsDead(people []person) {
for i := 0; i < len(people); i++ {
if people[i].age < 50 {
people[i].isDead = true
}
}
}
好, for range 部分讲到这里,接下来说一说 map 结构中值的传递和修改问题。
这段代码将之前的 people []person 改成了 map 结构,大家觉得有错误吗,如果有错,错在哪:
package main
import "fmt"
type person struct {
name string
age byte
isDead bool
}
func main() {
p1 := person{name: "zzy", age: 100}
p2 := person{name: "dj", age: 99}
p3 := person{name: "px", age: 20}
people := map[string]person{
p1.name: p1,
p2.name: p2,
p3.name: p3,
}
whoIsDead(people)
if p3.isDead {
fmt.Println("who is dead?", p3.name)
}
}
func whoIsDead(people map[string]person) {
for name, _ := range people {
if people[name].age < 50 {
people[name].isDead = true
}
}
}
go run 一下,报错:









