1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Go 1.20新變化!第一部分:語言特性

        共 6838字,需瀏覽 14分鐘

         ·

        2023-01-23 06:17

        又到了 Go 發(fā)布新版本的時刻了!

        2022 年第一季度的 Go 1.18 是一個主版本,它在語言中增加了期待已久的泛型,同時還有許多微小功能更新[1]優(yōu)化[2]。2022 年第三季度的 Go 1.19 是一個比較低調(diào)[3]的版本。

        現(xiàn)在是 2023 年,Go 1.20 RC 版本[4]已經(jīng)發(fā)布,而正式版本也即將到來,Go 團(tuán)隊已經(jīng)發(fā)布了版本說明草案[5]。

        在我看來,Go 1.20 的影響介于 1.18 和 1.19 之間,比 1.19 有更多的功能更新并解決了一些長期存在的問題,但沒有達(dá)到 1.18 中為語言增加泛型這樣的重磅規(guī)模。

        盡管如此,我還是要把我對“Go 1.20 的新變化”的看法分成系列三篇博文。

        首先,我寫了 Go 1.20 中的語言變化(如下),在下一篇文章中,我將寫標(biāo)準(zhǔn)庫的重要變化,最后一篇將講解 Go 1.20 中我最喜歡的對標(biāo)準(zhǔn)庫的小改動。
        那么,讓我們來看看語言方面的變化。首先,對泛型的規(guī)則做了一個小小的修改。有了 Go 泛型,你可以通過一個函數(shù)獲取任何map的鍵:
              
              func keys[K comparable, V any](m map[K]V) []K {
            var keys []K
            for k := range m {
                keys = append(keys, k)
            }
            return keys
        }
        ···

        在這段代碼中,K comparable, V any 為“類型約束”。這意味著 K 可以是任何 comparable 的類型,而 V 則沒有類型限制。comparable 類型為數(shù)字、布爾、字符串和由 comparable 元素組成的固定大小的復(fù)合類型等。因此,K 為 int,V 為一個 bytes 切片是合法的,但 K 是一個 bytes 切片是非法的。

        我說過上面的代碼會給你任何 map 的鍵,但在 Go 1.18 和 1.19 中,這并不是完全正確的。如果你試圖把它用在一個鍵值為接口類型的 map 上,它將不會被編譯。

              
              m := make(map[any]any) // ok
        keys(m)
        // 編譯器錯誤(Go 1.19):any 沒有實(shí)現(xiàn) comparable
        ···

        這個問題歸結(jié)為圍繞 K comparable 含義的解讀。要作為 map 鍵使用,類型必須被 Go 編譯器認(rèn)為是 comparable 的。例如,這是無效的:

              
              m := make(map[func()]any)
        // 編譯器錯誤:無效的 map 鍵類型 func()
        ···

        然而,你可以通過使用接口來得到一個運(yùn)行時錯誤而不是編譯器錯誤:

              
              m := make(map[any]any) // 正確
        k := func() {}
        m[k] = 1 // panic:運(yùn)行時錯誤:哈希值為不可哈希的類型 func()

        所以,像 any 這樣的接口類型是 map 的有效鍵類型,但如果你試圖把一個缺少有效類型定義的鍵放到 map 中,就會在運(yùn)行時出現(xiàn) panic 錯誤。顯然,沒有人希望他們的代碼在運(yùn)行時出現(xiàn) panic 錯誤,但這是在 map 中允許動態(tài)類型鍵的唯一方法。

        下面是一個從不同角度看同一問題的例子。假設(shè)我有一個這樣的錯誤類型:

              
              type myerr func() string

        func (m myerr) Error() string {
            return m()
        }

        而現(xiàn)在我想使用自定義的錯誤類型進(jìn)行比較:

              
              var err1 error = myerr(func() string { return "err1" })
        var err2 error = myerr(func() string { return "err2" })
        fmt.Println(err1 != nil, err2 != nil)  // true true

        fmt.Println(err1 == err2)
        // panic:運(yùn)行時錯誤:對 main.myerr 不可比類型進(jìn)行比較

        正如你所看到的,一個接口值在編譯時被認(rèn)為是 comparable 類型,但是如果它被賦的值是一個“不可比類型”,則在運(yùn)行時就會出現(xiàn) panic。如果你試圖比較兩個 http.Handler,而它們恰好都是 http.HandlerFuncs,你同樣可以看到這個問題。

        當(dāng) Go 1.18 支持了泛型后,大家發(fā)現(xiàn)[6],由于接口在編譯時被認(rèn)為是 ,但可能會包含不可比較的具體類型。如果你寫的泛型代碼的類型約束是 comparable,但錯誤的值被存儲在一個接口中,就有可能出現(xiàn)運(yùn)行時 panic。保守起見,Go 團(tuán)隊決定[7]在評估(此特性)的全部影響階段,Go 1.18 限制使用接口作為 comparable 類型。

        現(xiàn)在已經(jīng)過了一年了,也發(fā)布了兩個版本,經(jīng)過大量在 Github 上進(jìn)行的冗長討論[8],Go 團(tuán)隊認(rèn)為在通用代碼中使用接口作為 comparable 類型應(yīng)該是足夠安全的。如果你在 Go 1.20 中運(yùn)行 keys(map[any]any{}) ,它可以正常運(yùn)行,你不必考慮上面的任何說明。


        Go 1.20 中的另一個語言變化更容易解釋。如果你有一個切片,現(xiàn)在你可以很容易地將其轉(zhuǎn)換為一個固定長度的數(shù)組:
              
              s := []string{"a""b""c"}
        a := [3]string(s)

        如果切片比數(shù)組短,你會因越界而產(chǎn)生 panic:

              
              s := []int{1, 2, 3}
        a := [4]int(s)
        // panic: 運(yùn)行時錯誤: 不能將長度為 3 的切片轉(zhuǎn)換成長度為 4 的數(shù)組或數(shù)組指針

        這源于 Go 1.17 中增加的數(shù)組指針轉(zhuǎn)換特性:

              
              s := []string{"a""b""c"}
        p := (*[3]string)(s)

        在這種情況下,p 指向 s 定義的數(shù)組,因此修改一個就會修改另一個:

              
              s := []string{"a""b""c"}
        p := (*[3]string)(s)
        s[0] = "d"
        p[1] = "e"
        fmt.Println(s, p) // [d e c] &[d e c]

        另一方面,隨著 Go 1.20 中新增的切片轉(zhuǎn)換為數(shù)組特性,數(shù)組是 切片內(nèi)容的副本:

              
              s := []string{"a""b""c"}
        a := [3]string(s)
        s[0] = "d"
        a[1] = "e"
        fmt.Println(s, a)
        // [d b c] [a e c]
            
              



        除了將切片轉(zhuǎn)換為數(shù)組的語法外,Go 1.20 還為處理切片數(shù)據(jù)的 unsafe 包帶來了一些新增內(nèi)容。

        reflect 包一直有 reflect.SliceHeader[9] 和 reflect.StringHeader[10] ,它們是 Go 中切片和字符串的運(yùn)行時表示:

              
              type SliceHeader struct {
            Data uintptr
            Len  int
            Cap  int
        }

        type StringHeader struct {
            Data uintptr
            Len  int
        }

        reflect.SliceHeader 和 reflect.StringHeader 都有一個 Warning 提示:“它的表示方法可能在以后的版本中改變,因此不能確保障安全或可移植”,并且在試圖廢除它們[11]。誤用這些類型可能會導(dǎo)致代碼崩潰[12],但是在實(shí)踐中,很多程序都依賴于類似這樣的切片布局,很難想象 Go 團(tuán)隊會在沒有大量警告的情況下改變它,因?yàn)楹芏喑绦驎罎ⅰ?/span>

        為了給 Gopher 們提供一種官方支持的編寫不安全代碼的方式,Go 1.17 增加了unsafe.Slice[13],它允許你把任何指針變成一個切片(不管是否是個好主意)。

              
              obj := struct{ x, y, z int }{1, 2, 3}
        slice := unsafe.Slice(&obj.x, 3)
        obj.x = 4
        slice[1] = 5
        fmt.Println(obj, slice)
        // {4 5 3} [4 5 3]

        在 Go 1.20 中,還有 unsafe.SliceData[14](它返回一個指向切片數(shù)據(jù)的指針),unsafe.String[15](它以不安全的方式通過一個 byte 指針創(chuàng)建字符串),以及 unsafe.StringData[16](它以不安全的方式返回一個指向字符串?dāng)?shù)據(jù)的指針)。

        這些字符串函數(shù)是額外增加的不安全方式,因?yàn)樗鼈冊试S你違反 Go 的字符串不可變規(guī)則,但它也給了你很大的控制權(quán),可以在不分配新內(nèi)存的前提下轉(zhuǎn)換 byte 切片。

        這些工具像利刃一樣,好用卻很容易割傷自己。在語言中直接支持這些工具可能更好,而不是僅僅讓大家使用 unsafe.Pointer 來祈禱它奏效。

        用 Hank Hill 的話來形容,“無論你做什么,你都應(yīng)該以正確的方式去做,即使是錯誤的事情。[17]


        相關(guān)鏈接:

        [1] https://blog.carlmjohnson.net/post/2021/golang-118-minor-features/

        [2] https://blog.carlmjohnson.net/post/2022/golang-118-even-more-minor-features/

        [3] https://blog.carlmjohnson.net/post/2022/golang-119-new-features/

        [4] https://groups.google.com/g/golang-nuts/c/HMUAm5j5raw/m/va3dxBFyAgAJ

        [5] https://tip.golang.org/doc/go1.20

        [6] https://github.com/golang/go/issues/49587

        [7] https://github.com/golang/go/issues/50646

        [8] https://github.com/golang/go/issues/51338

        [9] https://pkg.go.dev/reflect#SliceHeader

        [10] https://pkg.go.dev/reflect#StringHeader

        [11] https://go-review.googlesource.com/c/go/+/401434

        [12] https://github.com/golang/go/issues/40701

        [13] https://pkg.go.dev/unsafe#Slice

        [14] https://pkg.go.dev/[email protected]#SliceData

        [15] https://pkg.go.dev/[email protected]#String

        [16] https://pkg.go.dev/[email protected]#StringData

        [17] https://www.getyarn.io/yarn-clip/08e52ddd-63ee-429b-b40c-b12c8ff6670b



        原文地址:

        https://blog.carlmjohnson.net/post/2023/golang-120-language-changes/

        原文作者:

        Carl M. Johnson

        本文永久鏈接: https://github.com/gocn/translator/blob/master/2023/w02_golang_120_language_changes.md 譯者 pseudoyu

        校對 :小超人


        往期推薦



        1b521a5f76eb453549f5fca2d50ac023.webp

        「每周譯Go」了解 Go 中的 init

        fb2869b6a10dee4073d9b2d3b46a0cb6.webp

        系統(tǒng)設(shè)計技巧:使用Postgres作為發(fā)布/訂閱和作業(yè)服務(wù)器


        c0775638716f603dcd910933da20b0c1.webp

        一文讀懂 Go Http Server 原理

        想要了解Go更多內(nèi)容,歡迎掃描下方??關(guān)注公眾號, 回復(fù)關(guān)鍵詞 [實(shí)戰(zhàn)群]   ,就有機(jī)會進(jìn)群和我們進(jìn)行交流

        分享、在看與點(diǎn)贊Go  ad962b6ead16ea38b20a29c2cb90f393.webp
        瀏覽 72
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            av五月天在线 | 操逼福利视频 | 女孩自慰网站 | 欧美性爱aaaa | 国模私拍在线视频一区二区 | 亚洲色图一区二区三区 | 国产成人主播 | 操老女人视频在线观看 | 8v天堂国产在线一区二区 | 久久久欧洲 |