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

          在 Go 語(yǔ)言中,有時(shí) nil 并不是一個(gè) nil

          共 2512字,需瀏覽 6分鐘

           ·

          2020-09-07 09:47

          點(diǎn)擊上方藍(lán)色“Go語(yǔ)言中文網(wǎng)”關(guān)注我們,領(lǐng)全套Go資料,每天學(xué)習(xí)?Go?語(yǔ)言

          今天,我遇到了一個(gè) Go FAQ[1]。首先,作為一個(gè)小小的 Go 語(yǔ)言測(cè)驗(yàn),看看您是否在 Go playground 中運(yùn)行該程序之前就能推斷出它應(yīng)該打印出的內(nèi)容(我已經(jīng)將程序放在側(cè)邊欄中,以防它在 Go playground 上消失)。該程序的關(guān)鍵代碼是:

          type?fake?struct?{?io.Writer?}

          func?fred?(logger?io.Writer)?{
          ???if?logger?!=?nil?{
          ??????logger.Write([]byte("..."))
          ???}
          }

          func?main()?{
          ???var?lp?*fake
          ???fred(nil)
          ???fred(lp)
          }

          由于 Go 語(yǔ)言中的變量是使用它們的零值顯式創(chuàng)建的,在指針的情況下,例如 lp 將會(huì)是 nil,您可能期待上述代碼會(huì)正常運(yùn)行(即不執(zhí)行任何操作)。實(shí)際上,它會(huì)在對(duì) fred() 的第二次調(diào)用時(shí)崩潰。原因是,在 Go 語(yǔ)言中,有時(shí)以 nil 為值的變量,如果直接打印的話,它雖然看起來(lái)像 nil,但實(shí)際上并不是真的 nil 。簡(jiǎn)而言之,Go 語(yǔ)言區(qū)別對(duì)待 nil 接口值和轉(zhuǎn)換為接口的值為 nil 的具體類型。只有前者確實(shí)為 nil,因此與字面上的 ni l 相等,就像 fred() 在這里做的一樣。

          (因此,可以使用 nil f 調(diào)用 (f *fake) 上的具體方法。它也許是一個(gè) nil 指針,但是它是類型化的 nil 指針,所以可以擁有有方法。甚至在接口轉(zhuǎn)換后依然可以擁有方法,正如上述的例子。)

          對(duì)于這里的情況,其解決方法是更改初始化的過(guò)程。實(shí)際的程序條件性地設(shè)置了 fake,類似于下面的代碼:

          var?l?*sLogger

          if?smtplog?!=?nil?{
          ????l?=?&sLogger
          ????l.prefix?=?logpref
          ????l.writer?=?bufio.NewWriterSize(smtplog,?4096)
          }
          convo?=?smtpd.NewConvo(conn,?l)

          這會(huì)將具體類型為 *sLoggernil 傳遞給期望參數(shù)為 io.Writer 的對(duì)象,從而導(dǎo)致接口轉(zhuǎn)換并掩蓋了 nil。為了解決這個(gè)問(wèn)題,我們可以添加一個(gè)必須顯式設(shè)置的中間變量 io.Writer

          var?l2?io.Writer

          if?smtplog?!=?nil?{
          ????l?:=?&sLogger
          ????l.prefix?=?logpref
          ????l.writer?=?....
          ????l2?=?l
          }
          convo?=?smtpd.NewConvo(conn,?l2)

          如果我們不初始化這個(gè)特殊的日志記錄器 sLogger,則 l2 會(huì)是一個(gè)真正的 io.Writer nil,并會(huì)在 smtpd 包中被檢測(cè)到。

          (您可以將類似的初始化操作封裝進(jìn)一個(gè)返回類型為 io.Writer 的函數(shù)中,并在沒(méi)有提供日志記錄器的情況下顯式返回 nil,通過(guò)這樣的技巧來(lái)達(dá)到類似的效果。需要強(qiáng)調(diào)的一點(diǎn)是,函數(shù)必須返回接口類型,如果返回類型為 *sLogger,那么您將再次遇到相同的問(wèn)題。)

          sLogger 的方法中保留對(duì)零值的防護(hù)代碼,這是一個(gè)個(gè)人喜好問(wèn)題。然而,我不想這么做,如果將來(lái)我在代碼中遇到類似的初始化錯(cuò)誤,我希望它崩潰,以便對(duì)其進(jìn)行修復(fù)。

          我從這件事中學(xué)到的另一個(gè)教訓(xùn)是,如果是出于調(diào)試的目的而進(jìn)行的打印,我不會(huì)再使用 %v 作為格式說(shuō)明符,而會(huì)使用 %#v。因?yàn)榍罢邔?huì)為接口 nil 和具體類型的 nil 同樣打印一個(gè)普通且具有誤導(dǎo)性的 ,而 %#v 將為前者打印出 ,為后者打印 (*main.fake)(nil)

          邊注欄: 測(cè)試程序

          package?main

          import?(
          ????"fmt"
          ????"io"
          )

          type?fake?struct?{
          ????io.Writer
          }

          func?fred(logger?io.Writer)?{
          ????if?logger?!=?nil?{
          ????????logger.Write([]byte("a?test\n"))
          ????}
          }

          func?main()?{
          ????//?這里的?t?的值是?nil
          ????var?t?*fake

          ????fred(nil)
          ????fmt.Printf("passed?1\n")
          ????fred(t)
          ????fmt.Printf("passed?2\n")
          }

          via: https://utcc.utoronto.ca/~cks/space/blog/programming/GoNilNotNil

          作者:ChrisSiebenmann[2]譯者:anxk[3]校對(duì):polaris1119[4]

          本文由 GCTT[5] 原創(chuàng)編譯,Go 中文網(wǎng)[6] 榮譽(yù)推出

          參考資料

          [1]

          Go FAQ: http://golang.org/doc/faq#nil_error

          [2]

          ChrisSiebenmann: https://utcc.utoronto.ca/~cks/space/People/ChrisSiebenmann

          [3]

          anxk: https://github.com/anxk

          [4]

          polaris1119: https://github.com/polaris1119

          [5]

          GCTT: https://github.com/studygolang/GCTT

          [6]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀

          • xxx


          學(xué)習(xí)交流 Go 語(yǔ)言,掃碼回復(fù)「進(jìn)群」即可


          站長(zhǎng) polarisxu

          自己的原創(chuàng)文章

          不限于 Go 技術(shù)

          職場(chǎng)和創(chuàng)業(yè)經(jīng)驗(yàn)


          Go語(yǔ)言中文網(wǎng)

          每天為你

          分享 Go 知識(shí)

          Go愛(ài)好者值得關(guān)注


          瀏覽 55
          點(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>
                  大伊人网站吗 | av77777 | 久久久久久久视频 | 欧美干逼视频 | 日韩精品熟妇 |