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

          Go1.17 新特性之切片變數(shù)組

          共 4051字,需瀏覽 9分鐘

           ·

          2021-07-27 06:49

          閱讀本文大概需要 6 分鐘。

          大家好,我是 polarisxu。

          按計(jì)劃,Go 1.17 會(huì)在 2021 年 8 月份發(fā)布(目前已經(jīng)發(fā)布了 Beta1 版本)。目前,1.17 相關(guān)的功能已經(jīng)開(kāi)發(fā)差不多了,上次介紹了測(cè)試順序隨機(jī)的問(wèn)題,今天介紹 1.17 中的另一個(gè)新功能:切片顯式地轉(zhuǎn)換成數(shù)組指針。

          溫馨提示,如果要試驗(yàn)該功能,需要升級(jí)到 1.17 Beta1 版本。另外一個(gè)主意事項(xiàng)就是如果在有 go.mod 的目錄中試驗(yàn),確保其中的版本改為 1.17,否則會(huì)報(bào)錯(cuò):conversion of slices to array pointers only supported as of -lang=go1.17

          01 數(shù)組轉(zhuǎn)切片

          介紹新功能之前,我們先看看在 Go 中如何將數(shù)組轉(zhuǎn)為切片。(當(dāng)然,數(shù)組指針也是 OK 的)

          一般地,通過(guò) slice 表達(dá)式(slice expressions)可以從一個(gè)數(shù)組得到一個(gè)切片。

          a[low : high : max]

          其中,max 可以省略。比如:

          a := [5]int{12345}
          s := a[1:4]

          s 就是一個(gè)切片。

          02 切片轉(zhuǎn)數(shù)組指針

          先了解下,為什么會(huì)有這樣的需求。

          該需求來(lái)自這個(gè) issue:https://github.com/golang/go/issues/395。rogpeppe 提到,很多時(shí)候,函數(shù)接收一個(gè) slice 參數(shù),但如果使用數(shù)組指針,則允許編譯器在編譯時(shí)檢查常量索引。比如這樣的情況:

          func foo(a []int) int {
              return a[0] + a[1] + a[2] + a[3];
          }

          能夠編譯期進(jìn)行索引檢查。比如這樣(當(dāng)然,最后實(shí)現(xiàn)不是這樣的):

          func foo(a []int) int {
              b := a.[0:4];
              return b[0] + b[1] + b[2] + b[3];
          }

          此外,有時(shí)候我們通過(guò)數(shù)組得到切片,但有時(shí)候我們直接創(chuàng)建切片,底層數(shù)組是匿名的。如果我們想要獲得底層數(shù)組怎么辦?將切片轉(zhuǎn)為數(shù)組指針可以實(shí)現(xiàn)這個(gè)需求。

          看看具體的例子,以下來(lái)自 Go 語(yǔ)言規(guī)范(針對(duì) Go1.17 這個(gè)語(yǔ)言特性新增):

          s := make([]byte24)
          s0 := (*[0]byte)(s)      // s0 != nil
          s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
          s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)

          var t []string
          t0 := (*[0]string)(t)    // t0 == nil
          t1 := (*[1]string)(t)    // panics: len([1]string) > len(s)

          幾個(gè)注意的點(diǎn):

          • 當(dāng)切片的長(zhǎng)度小于數(shù)組長(zhǎng)度(len)時(shí)會(huì) panic。所以上面例子中,s4 和 t1 發(fā)生了 panic
          • 將一個(gè)非空切片轉(zhuǎn)為 0 長(zhǎng)度的數(shù)組,得到的指針不是 nil(如 s0);但將一個(gè)空切片轉(zhuǎn)為 0 長(zhǎng)度的數(shù)組,得到的指針是 nil(如 t0);
          • 多次轉(zhuǎn)換,并不會(huì)創(chuàng)建多個(gè)數(shù)組(因?yàn)榈玫降氖堑讓訑?shù)組),這從 &s2[0] == &s[0] 可以看出;

          所以,總結(jié)一下就是,將切片轉(zhuǎn)換為數(shù)組指針,產(chǎn)生指向切片的底層數(shù)組的指針。如果切片的長(zhǎng)度小于數(shù)組的長(zhǎng)度,則會(huì)發(fā)生運(yùn)行時(shí) panic。

          不過(guò)針對(duì) panic,目前沒(méi)法做斷言檢查。只能通過(guò) if 判斷了。

          03 reflect 注意事項(xiàng)

          針對(duì)語(yǔ)言這個(gè)改動(dòng),reflect 包中的 Type 接口有一個(gè)方法:ConvertibleTo。之前的說(shuō)明是這樣的:

          // ConvertibleTo reports whether a value of the type is convertible to type u.
          ConvertibleTo(u Type) bool

          1.17 是這樣的:

          // ConvertibleTo reports whether a value of the type is convertible to type u.
          // Even if ConvertibleTo returns true, the conversion may still panic.
          // For example, a slice of type []T is convertible to *[N]T,
          // but the conversion will panic if its length is less than N.
          ConvertibleTo(u Type) bool

          因?yàn)榍衅D(zhuǎn)為數(shù)組指針可能會(huì) panic,所以才加了這么一句文檔說(shuō)明。

          因此,如果通過(guò)反射轉(zhuǎn)換做類(lèi)型轉(zhuǎn)換,雖然通過(guò) ConvertibleTo 判斷是可轉(zhuǎn)換的,但調(diào)用 Convert 方法依然可能 panic。這點(diǎn)需要特別注意下。

          04 小結(jié)

          這個(gè)語(yǔ)言改變,大部分時(shí)候可能用不到。但有些場(chǎng)景可以做到不需要內(nèi)存拷貝(copy),比如標(biāo)準(zhǔn)庫(kù)中有一個(gè)例子:

          // https://docs.studygolang.com/src/crypto/sha256/sha256.go?s=5787:5834#L252
          func Sum224(data []byte) (sum224 [Size224]byte) {
           var d digest
           d.is224 = true
           d.Reset()
           d.Write(data)
           sum := d.checkSum()
           copy(sum224[:], sum[:Size224])
           return
          }

          官方計(jì)劃修改為:

          func Sum224(data []byte) [Size224]byte {
           var d digest
           d.is224 = true
           d.Reset()
           d.Write(data)
           sum := d.checkSum()
           ap := (*[Size224]byte)(sum[:Size224])
           return *ap
          }

          注意其中的區(qū)別。

          但這里 bradfitz 在修改時(shí),發(fā)現(xiàn),為什么一定要轉(zhuǎn)為數(shù)組指針,能否直接轉(zhuǎn)為數(shù)組,畢竟,在 Go 中使用數(shù)組的話,不太常用數(shù)組指針。于是 bradfitz 給出了另一個(gè)提案:https://github.com/golang/go/issues/46505,即 allow conversion from slice to array。目前該提案是否接受,還沒(méi)有結(jié)論。




          往期推薦



          福利

          我為大家整理了一份從入門(mén)到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門(mén)看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 18
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  豆花视频综合网 | 精品麻豆一区二区国产明星 | 国产久视频| 精品国产丝袜白色高跟鞋 | 久久综合新金瓶梅一级黄大片 |