Go 語言重新手到大神:每一個人都會踩的五十個坑 (1-12)

本文翻譯自最近各類 Go 語言社區分享的不少的英文文檔 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs,小編第一眼覺得 50 Shades of Grey……html

Go語言是一個簡單卻蘊含深意的語言。可是,即使號稱是最簡單的C語言,都能總結出一本《C陷阱與缺陷》,更況且Go語言呢。Go語言中的許多坑其實並非由於Go自身的問題。一些錯誤你再別的語言中也會犯,例如做用域,一些錯誤就是對由於 Go 語言的特性不瞭解而致使的,例如 range。golang

其實若是你在學習Go語言的時候去認真地閱讀官方文檔,百科,郵件列表或者其餘的相似 Rob Pike 的名人博客,報告,那麼本文中提到的許多坑均可以免。可是不是每一個人都會從基礎學起,例如譯者就喜歡簡單粗暴地直接用Go語言寫程序。若是你也像譯者同樣,那麼你應該讀一下這篇文章:這樣能夠避免在調試程序時浪費過多時間。數組

本文將50個坑按照使用使用範圍和難易程度分爲如下三個級別:「新手入門級」,「新手深刻級」,「新手進階級」。數據結構

「{」不能單獨放在一行

級別:新手入門級app

Go語言設計者確定和C語言設計者(K&R)有種不明不白的關係,由於C語言中的K&R格式在Go語言中獲得發揚光大。大多數語言中,大括號中的左括號是能夠隨便放在哪裏的:C語言中必需要按照K&R格式對代碼進行格式化以後,左括號纔會被放在前一行中的最後。可是Go語言中,左括號必須強制不能單獨放在一行。這個規則得益於「自動分號注射」(automatic semicolon injection)。ide

補充:go提供了專門用於格式化代碼的gofmt工具。函數

出錯代碼:工具

package main

import "fmt"

func main()  
{ //error, can't have the opening brace on a separate line
    fmt.Println("hello there!")
}

錯誤信息:學習

/tmp/sandbox826898458/main.go:6: syntax error: unexpected semicolon or newline before {

修正代碼:翻譯

package main

import "fmt"

func main() {  
    fmt.Println("works!")
}

未使用已定義的變量

級別:新手入門級

若是代碼中有未使用的變量,那個代碼編譯的時候就會報錯。Go要求在代碼中全部聲明的變量都須要被用到,固然,全局變量除外。
函數的參數也能夠只被聲明,不被使用。

對於未聲明變量的調用一樣會致使編譯失敗。和C語言同樣,Go編譯器也是個女人,他說什麼你都要盡力知足。

出錯代碼:

package main

var gvar int //not an error

func main() {  
    var one int   //error, unused variable
    two := 2      //error, unused variable
    var three int //error, even though it's assigned 3 on the next line
    three = 3

    func(unused string) {
        fmt.Println("Unused arg. No compile error")
    }("what?")
}

錯誤信息:

/tmp/sandbox473116179/main.go:6: one declared and not used /tmp/sandbox473116179/main.go:7: two declared and not used /tmp/sandbox473116179/main.go:8: three declared and not used

修正代碼:

package main

import "fmt"

func main() {  
    var one int
    _ = one

    two := 2 
    fmt.Println(two)

    var three int 
    three = 3
    one = three

    var four int
    four = four
}

固然,你也能夠考慮刪除那些沒有使用的變量。

未使用的包

級別:新手入門級

當import一個包以後,若是不使用這個包,或者這個包中的函數/接口/數據結構/變量,那麼將會編譯失敗。

若是真的確認要引入變量可是不使用的話,咱們能夠用「」標識符座標記,避免編譯失敗。「」標識符表示爲了獲得這些包的反作用而引入這些包。

出錯代碼:

package main

import (  
    "fmt"
    "log"
    "time"
)

func main() {  
}

錯誤信息:

/tmp/sandbox627475386/main.go:4: imported and not used: "fmt" 
/tmp/sandbox627475386/main.go:5: imported and not used: "log" 
/tmp/sandbox627475386/main.go:6: imported and not used: "time"

修正代碼

package main

import (  
    _ "fmt"
    "log"
    "time"
)

var _ = log.Println

func main() {  
    _ = time.Now
}

只能在函數內部使用簡短的變量聲明

級別:新手入門級
出錯代碼:

package main

myvar := 1 //error

func main() {  
}

錯誤信息:

/tmp/sandbox265716165/main.go:3: non-declaration statement outside function body

修正代碼:

package main

var myvar = 1

func main() {  
}

沒法使用精簡的賦值語句對變量從新賦值

級別:新手入門級

不能使用精簡的賦值語句從新賦值單個變量,可是可使用精簡的賦值語句同時賦值多個變量。

而且,重定義的變量必須寫在同一個代碼塊。

錯誤信息:

package main

func main() {  
    one := 0
    one := 1 //error
}

錯誤信息:

/tmp/sandbox706333626/main.go:5: no new variables on left side of :=

修正代碼:

package main

func main() {  
    one := 0
    one, two := 1,2

    one,two = two,one
}

隱式變量(做用域)

級別:新手入門級

和 C 語言同樣,Go 語言也有做用於,一個變量的做用範圍僅僅是一個代碼塊。雖然精簡的賦值語句很簡單,可是注意做用域。

package main

import "fmt"

func main() {  
    x := 1
    fmt.Println(x)     //打印 1
    {
        fmt.Println(x) //打印 1
        x := 2
        fmt.Println(x) //打印 2
    }
    fmt.Println(x)     //打印 1 ( 不是 2)
}

甚至對於有經驗的開發者來講,這也是個不注意就會掉進去的深坑。

除非特別指定,不然沒法使用 nil 對變量賦值

級別:新手入門級

nil 能夠用做 interface、function、pointer、map、slice 和 channel 的「空值」。可是若是不特別指定的話,Go 語言不能識別類型,因此會報錯。

錯誤信息:

package main

func main() {  
    var x = nil //error

    _ = x
}

錯誤信息:

/tmp/sandbox188239583/main.go:4: use of untyped nil

修正代碼:

package main

func main() {  
    var x interface{} = nil

    _ = x
}

Slice 和 Map 的 nil 值

級別:新手入門級

初始值爲 nil 的 Slice 是能夠進行「添加」操做的,可是對於 Map 的「添加」操做會致使運行時恐慌。( ﹁ ﹁ ) 恐慌。

修正代碼:

package main

func main() {  
    var s []int
    s = append(s,1)
}

錯誤信息:

package main

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

}

Map 定長

級別:新手入門級

建立 Map 的時候能夠指定 Map 的長度,可是在運行時是沒法使用 cap() 功能從新指定 Map 的大小,Map 是定長的。

錯誤信息:

package main

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

錯誤信息:

/tmp/sandbox326543983/main.go:5: invalid argument m (type map[string]int) for cap

字符串沒法爲 nil

級別:新手入門級

全部的開發者均可能踩的坑,在 C 語言中是能夠 char *String=NULL,可是 Go 語言中就沒法賦值爲 nil。

錯誤信息:

package main

func main() {  
    var x string = nil //error

    if x == nil { //error
        x = "default"
    }
}

Compile Errors:

/tmp/sandbox630560459/main.go:4: cannot use nil as type string in assignment /tmp/sandbox630560459/main.go:6: invalid operation: x == nil (mismatched types string and nil)

修正代碼:

package main

func main() {  
    var x string //defaults to "" (zero value)

    if x == "" {
        x = "default"
    }
}

參數中的數組

Array Function Arguments

級別:新手入門級
對於 C 和 C++ 開發者來講,數組就是指針。給函數傳遞數組就是傳遞內存地址,對數組的修改就是對原地址數據的修改。可是 Go 語言中,傳遞的數組不是內存地址,而是原數組的拷貝,因此是沒法經過傳遞數組的方法去修改原地址的數據的。

package main

import "fmt"

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])
}

若是須要修改原數組的數據,須要使用數組指針(array pointer)。

package main

import "fmt"

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]
}

或者可使用 Slice,

package main

import "fmt"

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]
}

使用 Slice 和 Array 的 range 會致使預料外的結果

級別:新手入門級

若是你對別的語言中的 for inforeach 熟悉的話,那麼 Go 中的 range 使用方法徹底不同。由於每次的 range 都會返回兩個值,第一個值是在 Slice 和 Array 中的編號,第二個是對應的數據。

出錯代碼:

package main

import "fmt"

func main() {  
    x := []string{"a","b","c"}

    for v := range x {
        fmt.Println(v) //prints 0, 1, 2
    }
}

修正代碼:

package main

import "fmt"

func main() {  
    x := []string{"a","b","c"}

    for _, v := range x {
        fmt.Println(v) //prints a, b, c
    }
}

( ﹁ ﹁ ) 怎麼有「大神」這個標籤…… 本文續集會在今晚八點前發佈,敬請期待。

相關文章
相關標籤/搜索