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

Slice 和 Array 維度是一維

級別:新手入門級安全

Go 看上去支持多維的 Array 和 Slice,可是其實否則。儘管能夠建立 Array 的 Array,也能夠建立 Slice 的 Slice。對於依賴多維 Array 的計算密集型的程序,不管是從性能仍是複雜程度,Go 都不是最佳選擇。數據結構

固然,若是你選擇建立嵌套的 Array 與嵌套的 Slice,那麼你就得本身負責進行索引、進行下表檢查、以及 Array 增加時的內存分配。嵌套 Slice 分爲兩種,Slice 中嵌套獨立的 Slice,或者 Slice 中嵌套共享數據的 Slice。併發

使用嵌套的獨立 Slice 建立多維的 Array 須要兩步。第一步,建立外圍 Slice,而後分配每一個內部的 Slice。內部的 Slice 是獨立的,能夠對每一個單獨的內部 Slice 進行縮放。app

package main

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

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

使用嵌套、共享數據的 Slice 建立多維 Array 須要三步。第一,建立數據「容器」,第二部,建立外圍 Slice,第三部,對內部的 Slice 進行初始化。ide

package main

import "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] <ptr_addr_x>

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

Go 語言也有對於支持多維 Array 和 Slice 的提案,不過不要期待太多。Go 語言官方將這些需求分在「低優先級」組中。高併發

試圖訪問不存在的 Map 鍵值

級別:新手入門級性能

並不能在全部狀況下都能經過判斷 map 的記錄值是否是 nil 判斷記錄是否存在。在 Go 語言中,對於「零值」是 nil 的數據類型能夠這樣判斷,可是其餘的數據類型不能夠。簡而言之,這種作法並不可靠(例如布爾變量的「零值」是 false)。最可靠的作法是檢查 map 記錄的第二返回值。
錯誤代碼:優化

package main

import "fmt"

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

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

修正代碼:ui

package main

import "fmt"

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

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

String 不可變

級別:新手入門級日誌

對於 String 中單個字符的操做會致使編譯失敗。String 是帶有一些附加屬性的只讀的字節片(Byte Slices)。因此若是想要對 String 操做的話,應當使用字節片操做,而不是將它轉換爲 String 類型。

錯誤信息:

package main

import "fmt"

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

    fmt.Println(x)
}

錯誤信息:

/tmp/sandbox305565531/main.go:7: cannot assign to x[0]

修正代碼:

package main

import "fmt"

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

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

注意這裏的操做並不就是最正確的操做,由於有些字符可能會存儲在多個字節中。若是你的開發情景有這種狀況的話,須要先把 String 轉換爲 rune 格式。即使是 rune,一個字符也可能會保存在多個 rune 中,所以 Go String 也表現爲字節序列(String Sequences)。Rune 通常理解爲「字符」,「一個字符也可能會保存在多個 rune 中」的狀況咱們將會在下面提到。

String 與 Byte Slice 的轉換

級別:新手入門級

當將 String 類型和 Byte Slice 類型互相轉化的時候,獲得的新數據都是原數據的拷貝,而不是原數據。類型轉化和切片重組(Resliciing)不同,切片重組後的變量仍然指向原變量,而類型轉換後的變量指向原變量的拷貝。

Go 語言已經對 []byte 和 String 類型的互相轉化作了優化,而且還會繼續優化。

The first optimization avoids extra allocations when []byte keys are used to lookup entries in map[string] collections: m[string(key)].
一個優化是

The second optimization avoids extra allocations in for range clauses where strings are converted to []byte: for i,v := range []byte(str) {...}.

String 與下標

級別:新手入門級

和其餘語言不一樣,String 的下表返回值是 Byte 類型的值,而不是字符類型。

package main

import "fmt"

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

若是須要在 UTF8 類型的 String 中取出指定字符,那麼須要用到 unicode/utf8 與實驗性的 utf8string 包。utf8string 包包含 AT() 方法,能夠取出字符,也能夠將 String 轉換爲 Rune SLice。

String並不必定是UTF8格式

級別:新手入門級

String 類型不必定是 UTF8 格式,String 中也能夠包含自定義的文字/字節。只有須要將字符串顯示出來的時候才須要用 UTF8 格式,其餘狀況下能夠隨便用轉義來表示任意字符。

可使用 unicode/utf8 包中體重的 ValidString() 方法判斷是不是 UTF8 類型的文本。

package main

import (  
    "fmt"
    "unicode/utf8"
)

func main() {  
    data1 := "ABC"
    fmt.Println(utf8.ValidString(data1)) //prints: true

    data2 := "A\xfeC"
    fmt.Println(utf8.ValidString(data2)) //prints: false
}

String 長度

級別:新手入門級

Python 開發者的代碼:

data = u'♥'  
print(len(data)) #prints: 1

轉換成相似的 Go 代碼以下:

package main

import "fmt"

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

Go 的 len() 方法和 Python 的並不相同,和 Python 的 len 方法等價的 Go 方法是 RuneCountInString。

package main

import (  
    "fmt"
    "unicode/utf8"
)

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

固然有些狀況(例如法語)的狀況,RuneCountInString 也並不能徹底返回字符數目,由於有些字符是使用多個字符的方式進行存儲的。

package main

import (  
    "fmt"
    "unicode/utf8"
)

func main() {  
    data := "é"
    fmt.Println(len(data))                    //prints: 3
    fmt.Println(utf8.RuneCountInString(data)) //prints: 2
}

多行的 Slice Arry 和 Map 賦值中缺乏逗號

Missing Comma In Multi-Line Slice, Array, and Map Literals

級別:新手入門級
錯誤信息:

package main

func main() {  
    x := []int{
    1,
    2 //error
    }
    _ = x
}

Compile Errors:

/tmp/sandbox367520156/main.go:6: syntax error: need trailing comma before newline in composite literal /tmp/sandbox367520156/main.go:8: non-declaration statement outside function body /tmp/sandbox367520156/main.go:9: syntax error: unexpected }

修正代碼:

package main

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

    y := []int{3,4,} //no error
    y = y
}

固然,若是把這些東西寫在一行中,能夠不加最後的逗號。

log.Fatal 與 log.Panic 在後臺悄悄作了一些事情

級別:新手入門級

日誌庫提供了不一樣級別的日誌記錄,若是使用 Fatal 和 Panic 級別的日誌,那麼記錄完這條日誌後,應用程序便會退出而不會繼續執行。

package main

import "log"

func main() {  
    log.Fatalln("Fatal Level: log entry") //app exits here
    log.Println("Normal Level: log entry")
}

內置的數據結構操做是無鎖的

級別:新手入門級

雖然 Go 語言天生高併發,可是 Go 並無考慮到數據安全。爲了實現「原子化」的數據操做,開發者須要本身對數據操做進行加鎖。固然, goroutine 和 channel 以及 sync 都是手動加鎖的好方案。

相關文章
相關標籤/搜索