Go语言新手容易犯下的错误

图片来源于网络

Map

1、使用nil map❌

func main() {  
    var m map[string]int
    m["one"] = 1 //error

}

2、指定map容量❌

func main() {  
    m := make(map[string]int,99)
    cap(m) //error
}

3、访问map不存在的key❌

错误❌的方式

func main() {  
    x := map[string]string{"one":"a","two":"","three":"c"}

    if v := x["two"]; v == "" { //incorrect
        fmt.Println("no entry")
    }
}

正确✅的方式

func main() {  
    x := map[string]string{"one":"a","two":"","three":"c"}

    if _,ok := x["two"]; !ok {
        fmt.Println("no entry")
    }
}

4、认为遍历map是有序的

你以为他会one、two、three、four这样输出,那么你就错❌啦

func main() {  
    m := map[string]int{"one":1,"two":2,"three":3,"four":4}
    for k,v := range m {
        fmt.Println(k,v)
    }
}

想按顺序输出,可以参考下面??的方式

 【Go语言陷阱】遍历map无法顺序输出?

Array/Slice

1、数组函数参数

如果你想改变数组的成员,错误❌的用法

func main() {  
    x := [3]int{1,2,3}

    func(arr [3]int) {
        arr[0] = 7
        fmt.Println(arr) //prints [7 2 3]
    }(x)

    fmt.Println(x) //prints [1 2 3] (not ok if you need [7 2 3])
}

如果你想改变数组的成员,正确✅的用法

func main() {  
    x := [3]int{1,2,3}

    func(arr *[3]int) {
        (*arr)[0] = 7
        fmt.Println(arr) //prints &[7 2 3]
    }(&x)

    fmt.Println(x) //prints [7 2 3]
}

亦或者使用切片

func main() {  
    x := []int{1,2,3}

    func(arr []int) {
        arr[0] = 7
        fmt.Println(arr) //prints [7 2 3]
    }(x)

    fmt.Println(x) //prints [7 2 3]
}

2、创建多维数组的正确方式✅

func main() {  
    x := 2
    y := 4

    table := make([][]int,x)
    for i:= range table {
        table[i] = make([]int,y)
    }
}

String

1.修改字符串❌

在Go语言中,String是只读的

错误❌wu的方式

func main() {  
    x := "text"
    x[0] = 'T'

    fmt.Println(x)
}

正确✅的方式

func main() {  
    x := "text"
    xbytes := []byte(x)
    xbytes[0] = 'T'

    fmt.Println(string(xbytes)) //prints Text
}

2、索引字符串

索引字符串返回的是byte而不是整个字符

func main() {  
	x := "我text"
	fmt.Println(x[0]) //print 230
	fmt.Println(string(x[0])) //print æ
	fmt.Printf("%T",x[0]) //prints uint8
}

如果想要想访问整个字符使用 for range

func main()  {
	x := "我text"
	for _,v := range x {
		fmt.Printf("%c",v) //prints uint8
	}
}

3、字符串的长度

当我们想统计字符串的长度的时候

使用len去统计字符串长度的时候会懵逼❌

func main() {  
    data := "♥"
    fmt.Println(len(data)) //prints: 3
}

正确✅统计字符串长度的方式

func main() {  
    data := "♥"
    fmt.Println(utf8.RuneCountInString(data)) //prints: 1
}

Int

1、自增和自减

在很多都会支持++i和i++这种方式的自增或者自减

Go语言中不支持++i这种前缀自增和自减方式,也不能按照下面的方式使用

func main() {  
    data := []int{1,2,3}
    i := 0
    ++i //error
    fmt.Println(data[i++]) //error
}

Goroutine

1、程序不会等待所有Goroutine结束

错误❌的方式

func main() {  
    workerCount := 2

    for i := 0; i < workerCount; i++ {
        go doit(i)
    }
    time.Sleep(1 * time.Second)
    fmt.Println("all done!")
}

func doit(workerId int) {  
    fmt.Printf("[%v] is running\n",workerId)
    time.Sleep(3 * time.Second)
    fmt.Printf("[%v] is done\n",workerId)
}

你会看到

[0] is running
[1] is running
all done!

因为在Go中,并不会等待所有goroutine运行结束,程序才会结束

如果想等待所有goroutine运行结束,才结束程序,那么得使用sync.WaitGroup 

看下正确✅的姿势

func main() {
	var wg sync.WaitGroup
	workerCount := 2

	for i := 0; i < workerCount; i++ {
		wg.Add(1)
		go doit(i,&wg)
	}
	wg.Wait()
	fmt.Println("all done!")
}

func doit(workerId int,wg *sync.WaitGroup) {
	fmt.Printf("[%v] is running\n",workerId)
	defer wg.Done()
	fmt.Printf("[%v] is done\n",workerId)
}

正常的输出

[1] is running
[1] is done
[0] is running
[0] is done
all done!

 Channel

1、向已经关闭的chan发送数据

func main() {
	ch := make(chan int)
	for i := 0; i < 3; i++ {
		go func(idx int) {
			ch <- (idx + 1) * 2
		}(i)
	}

	//get the first result
	fmt.Println(<-ch)
	close(ch) //not ok (you still have other senders)
	//do other work
	time.Sleep(2 * time.Second)
}

2、nil chan发送或接收数据

func main() {  
    var ch chan int
    for i := 0; i < 3; i++ {
        go func(idx int) {
            ch <- (idx + 1) * 2
        }(i)
    }

    //get first result
    fmt.Println("result:",<-ch)
    //do other work
    time.Sleep(2 * time.Second)
}

References

50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

 


版权声明:本文为qq_37186127原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。