<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang 新手要注意的陷阱和常見錯誤(一)

          共 7792字,需瀏覽 16分鐘

           ·

          2020-08-20 14:11

          Go 是一門簡單有趣的語言,但與其他語言類似,它會有一些技巧。。。這些技巧的絕大部分并不是 Go 的缺陷造成的。如果你以前使用的是其他語言,那么這其中的有些錯誤就是很自然的陷阱。其它的是由錯誤的假設(shè)和缺少細節(jié)造成的。

          如果你花時間學(xué)習(xí)這門語言,閱讀官方說明、wiki、郵件列表討論、大量的優(yōu)秀博文和 Rob Pike 的展示,以及源代碼,這些技巧中的絕大多數(shù)都是顯而易見的。盡管不是每個人都是以這種方式開始學(xué)習(xí)的,但也沒關(guān)系。如果你是 Go 語言新人,那么這里的信息將會節(jié)約你大量的調(diào)試代碼的時間。

          一. 初級篇

          1. 開大括號不能放在單獨的一行

          在大多數(shù)其他使用大括號的語言中,你需要選擇放置它們的位置。Go 的方式不同。你可以為此感謝下自動分號的注入(沒有預(yù)讀)。是的, Go 中也是有分號的:-)
          失敗的例子:

          package mainimport "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 mainimport "fmt"func main() {      fmt.Println("works!")}


          2. 未使用的變量

          如果你有未使用的變量,代碼將編譯失敗。當(dāng)然也有例外。在函數(shù)內(nèi)一定要使用聲明的變量,但未使用的全局變量是沒問題的。
          如果你給未使用的變量分配了一個新的值,代碼還是會編譯失敗。你需要在某個地方使用這個變量,才能讓編譯器愉快的編譯。
          Fails:

          package mainvar gvar int //not an errorfunc 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     }

          Compile Errors:

          /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

          Works:

          package mainimport "fmt"func main() {      var one int    _ = one    two := 2     fmt.Println(two)    var three int     three = 3    one = three    var four int    four = four}

          另一個選擇是注釋掉或者移除未使用的變量?


          3. 未使用的 import

          如果你引入一個包,而沒有使用其中的任何函數(shù)、接口、結(jié)構(gòu)體或者變量的話,代碼將會編譯失敗。
          你可以使用 goimports 來增加引入或者移除未使用的引用:

          $ go get golang.org/x/tools/cmd/goimports

          如果你真的需要引入的包,你可以添加一個下劃線標(biāo)記符 _ ,來作為這個包的名字,從而避免編譯失敗。下滑線標(biāo)記符用于引入,但不使用。

          package mainimport (    "fmt"    "log"    "time")func main() {}
          Compile Errors:
          /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"

          Works:

          package mainimport (    _ "fmt"    "log"    "time")var _ = log.Printlnfunc main() {    _ = time.Now}

          另一個選擇是移除或者注釋掉未使用的 import

          4. 簡式的變量聲明僅可以在函數(shù)內(nèi)部使用

          Fails:

          package mainmyvar := 1 //errorfunc main() {}

          Compile Error:

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

          Works:

          package mainvar myvar = 1func main() {}

          5. 使用簡式聲明重復(fù)聲明變量

          你不能在一個單獨的聲明中重復(fù)聲明一個變量,但在多變量聲明中這是允許的,其中至少要有一個新的聲明變量。
          重復(fù)變量需要在相同的代碼塊內(nèi),否則你將得到一個隱藏變量。
          Fails:

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

          Compile Error:

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

          Works:

          package mainfunc main() {    one := 0    one, two := 1,2    one,two = two,one}

          6. 偶然的變量隱藏 Accidental Variable Shadowing

          短式變量聲明的語法如此的方便(尤其對于那些使用過動態(tài)語言的開發(fā)者而言),很容易讓人把它當(dāng)成一個正常的分配操作。如果你在一個新的代碼塊中犯了這個錯誤,將不會出現(xiàn)編譯錯誤,但你的應(yīng)用將不會做你所期望的事情。

          package mainimport "fmt"func main() {    x := 1    fmt.Println(x)     //prints 1    {        fmt.Println(x) //prints 1        x := 2        fmt.Println(x) //prints 2    }    fmt.Println(x)     //prints 1 (bad if you need 2)}
          即使對于經(jīng)驗豐富的Go開發(fā)者而言,這也是一個非常常見的陷阱。這個坑很容易挖,但又很難發(fā)現(xiàn)。

          你可以使用 vet 命令來發(fā)現(xiàn)一些這樣的問題。默認情況下, vet不會執(zhí)行這樣的檢查,你需要設(shè)置 -shadow參數(shù):
          go tool vet -shadow your_file.go


          7. 不使用顯式類型,無法使用“nil”來初始化變量

          nil 標(biāo)志符用于表示 interface 、函數(shù)、 mapssliceschannels 的“零值”。如果你不指定變量的類型,編譯器將無法編譯你的代碼,因為它猜不出具體的類型。
          Fails:

          package mainfunc main() {    var x = nil //error    _ = x}
          Compile Error:
          /tmp/sandbox188239583/main.go:4: use of untyped nil

          Works:

          package mainfunc main() {    var x interface{} = nil    _ = x}

          8. 使用“nil” Slices and Maps

          在一個 nilslice 中添加元素是沒問題的,但對一個 map 做同樣的事將會生成一個運行時的 panic
          Works:

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

          Fails:

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

          9. map的容量

          你可以在 map 創(chuàng)建時指定它的容量,但你無法在 map 上使用 cap() 函數(shù)。
          Fails:

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

          Compile Error:

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


          10. 字符串不會為nil

          這對于經(jīng)常使用 nil 分配字符串變量的開發(fā)者而言是個需要注意的地方。
          Fails:

          package mainfunc 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)

          Works:

          package mainfunc main() {    var x string //defaults to "" (zero value)    if x == "" {        x = "default"    }}

          11. array 函數(shù)的參數(shù)

          如果你是一個 C 或則 C++ 開發(fā)者,那么數(shù)組對你而言就是指針當(dāng)你向函數(shù)中傳遞數(shù)組時,函數(shù)會參照相同的內(nèi)存區(qū)域,這樣它們就可以修改原始的數(shù)據(jù)

          Go 中的數(shù)組是數(shù)值,因此當(dāng)你向函數(shù)中傳遞數(shù)組時,函數(shù)會得到原始數(shù)組數(shù)據(jù)的一份復(fù)制。如果你打算更新數(shù)組的數(shù)據(jù),這將會是個問題。

          package mainimport "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])}

          如果你需要更新原始數(shù)組的數(shù)據(jù),你可以使用數(shù)組指針類型。

          package mainimport "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 。即使你的函數(shù)得到了 slice 變量的一份拷貝,它依舊會參照原始的數(shù)據(jù)。

          package mainimport "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]}


          12. 在 slice 和 array 使用“range”語句時的出現(xiàn)的不希望得到的值

          如果你在其他的語言中使用 for-in 或者 foreach 語句時會發(fā)生這種情況。Go 中的 range 語法不太一樣。它會得到兩個值:第一個值是元素的索引,而另一個值是元素的數(shù)據(jù)。
          Bad:

          package mainimport "fmt"func main() {    x := []string{"a","b","c"}    for v := range x {        fmt.Println(v) //prints 0, 1, 2    }}

          Good:

          package mainimport "fmt"func main() {    x := []string{"a","b","c"}    for _, v := range x {        fmt.Println(v) //prints a, b, c    }}


          13. slices 和 arrays 是一維的

          看起來 Go 好像支持多維的 ArraySlice ,但不是這樣的。盡管可以創(chuàng)建數(shù)組的數(shù)組或者切片的切片。對于依賴于動態(tài)多維數(shù)組的數(shù)值計算應(yīng)用而言, Go 在性能和復(fù)雜度上還相距甚遠。

          你可以使用純一維數(shù)組、“獨立”切片的切片,“共享數(shù)據(jù)”切片的切片來構(gòu)建動態(tài)的多維數(shù)組。
          如果你使用純一維的數(shù)組,你需要處理索引、邊界檢查、當(dāng)數(shù)組需要變大時的內(nèi)存重新分配。

          使用“獨立” slice 來創(chuàng)建一個動態(tài)的多維數(shù)組需要兩步。首先,你需要創(chuàng)建一個外部的 slice 。然后,你需要分配每個內(nèi)部的 slice 。內(nèi)部的 slice 相互之間獨立。你可以增加減少它們,而不會影響其他內(nèi)部的 slice

          package mainfunc main() {    x := 2    y := 4    table := make([][]int,x)    for i:= range table {        table[i] = make([]int,y)    }}

          使用“共享數(shù)據(jù)” sliceslice 來創(chuàng)建一個動態(tài)的多維數(shù)組需要三步。首先,你需要創(chuàng)建一個用于存放原始數(shù)據(jù)的數(shù)據(jù)“容器”。然后,你再創(chuàng)建外部的 slice 。最后,通過重新切片原始數(shù)據(jù) slice 來初始化各個內(nèi)部的 slice

          package mainimport "fmt"func main() {    h, w := 2, 4    raw := make([]int,h*w)    for i := range raw {        raw[i] = i    }    fmt.Println(raw,&raw[4])    //prints: [0 1 2 3 4 5 6 7]     table := make([][]int,h)    for i:= range table {        table[i] = raw[i*w:i*w + w]    }    fmt.Println(table,&table[1][0])    //prints: [[0 1 2 3] [4 5 6 7]] }

          關(guān)于多維 arrayslice 已經(jīng)有了專門申請,但現(xiàn)在看起來這是個低優(yōu)先級的特性。


          14. 訪問不存在的 map keys

          這對于那些希望得到 nil 標(biāo)示符的開發(fā)者而言是個技巧(和其他語言中做的一樣)。如果對應(yīng)的數(shù)據(jù)類型的“零值”是 nil ,那返回的值將會是 nil ,但對于其他的數(shù)據(jù)類型是不一樣的。檢測對應(yīng)的“零值”可以用于確定 map 中的記錄是否存在,但這并不總是可信(比如,如果在二值的 map 中“零值”是 false ,這時你要怎么做)。檢測給定 map 中的記錄是否存在的最可信的方法是,通過 map 的訪問操作,檢查第二個返回的值。
          Bad:

          package mainimport "fmt"func main() {    x := map[string]string{"one":"a","two":"","three":"c"}    if v := x["two"]; v == "" { //incorrect        fmt.Println("no entry")    }}

          Good:

          package mainimport "fmt"func main() {    x := map[string]string{"one":"a","two":"","three":"c"}    if _,ok := x["two"]; !ok {        fmt.Println("no entry")    }}


          15. Strings 無法修改

          嘗試使用索引操作來更新字符串變量中的單個字符將會失敗。string 是只讀的 byte slice (和一些額外的屬性)。如果你確實需要更新一個字符串,那么使用 byte slice ,并在需要時把它轉(zhuǎn)換為 string 類型。
          Fails:

          package mainimport "fmt"func main() {    x := "text"    x[0] = 'T'    fmt.Println(x)}
          Compile Error:
          /tmp/sandbox305565531/main.go:7:?cannot?assign?to?x[0]

          Works:

          package mainimport "fmt"func main() {    x := "text"    xbytes := []byte(x)    xbytes[0] = 'T'    fmt.Println(string(xbytes)) //prints Text}
          需要注意的是:這并不是在文字 string 中更新字符的正確方式,因為給定的字符可能會存儲在多個 byte 中。如果你確實需要更新一個文字 string ,先把它轉(zhuǎn)換為一個 rune slice

          即使使用 rune slice ,單個字符也可能會占據(jù)多個 rune ,比如當(dāng)你的字符有特定的重音符號時就是這種情況。這種復(fù)雜又模糊的“字符”本質(zhì)是 Go 字符串使用 byte 序列表示的原因。

          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩特级黄色电影 | 国产a级久久 | 草无码视频 | 亚州V| 豆花av资源 |