golang新手们容易犯的3个错误总结

2020-01-28 13:13:28于海丽

我相信很多人一看就看出问题在哪了,但肯定还有人不清楚 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 一下,报错: