亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

隱藏 nil 值,理解為什么 golang 在這里失敗

隱藏 nil 值,理解為什么 golang 在這里失敗

Go
郎朗坤 2021-09-21 20:50:13
我無法理解如何正確確保nil在這種情況下不是:package maintype shower interface {  getWater() []shower}type display struct {  SubDisplay *display}func (d display) getWater() []shower {  return []shower{display{}, d.SubDisplay}}func main() {  // SubDisplay will be initialized with null  s := display{}  // water := []shower{nil}  water := s.getWater()  for _, x := range water {    if x == nil {      panic("everything ok, nil found")    }    //first iteration display{} is not nil and will    //therefore work, on the second iteration    //x is nil, and getWater panics.    x.getWater()  }}我發現檢查該值是否實際的唯一方法nil是使用反射。這真的是想要的行為嗎?或者我沒有在我的代碼中看到一些重大錯誤?
查看完整描述

3 回答

?
倚天杖

TA貢獻1828條經驗 獲得超3個贊

這里的問題是這shower是一種interface類型。Go 中的接口類型保存實際值及其動態類型。有關此的更多詳細信息:反射定律#接口的表示。


您返回的切片包含 2 個非nil值。第二個值是一個接口值,一個 (value;type) 對,包含一個nil指針值和一個*display具體類型。引用Go 語言規范:比較運算符:


接口值具有可比性。如果兩個接口值具有相同的動態類型和相同的動態值,或者兩者都具有 value,則它們相等nil。


所以,如果你把它比作nil,這將是false。如果將它與表示對的接口值進行比較(nil;*display),它將是true:


if x == (*display)(nil) {

    panic("everything ok, nil found")

}

這似乎不可行,因為您必須知道接口擁有的實際類型。但請注意,您可以使用反射來判斷非nil接口值是否nil使用包裝了一個值Value.IsNil()。你可以在Go Playground上看到一個這樣的例子。


為什么以這種方式實施?


與其他具體類型(非接口)不同的接口可以保存不同具體類型(不同靜態類型)的值。運行時需要知道存儲在接口類型變量中的值的動態或運行時類型。


Aninterface只是一個方法集,如果相同的方法是該類型的方法集的一部分,則任何類型都會實現它。有些類型不能是,例如 a或自定義類型作為其基礎類型。在這些情況下,您不需要能夠存儲該特定類型的值。nilstructintnil


但是任何類型也包括其中nil是有效值的具體類型(例如切片、映射、通道、所有指針類型),因此為了在運行時存儲滿足接口的值,支持存儲nil在接口內部是合理的。但是除了nil接口內部,我們必須存儲它的動態類型,因為nil值不攜帶此類信息。另一種選擇是nil當要存儲在其中的值是 時,將其本身用作接口值nil,但這種解決方案是不夠的,因為它會丟失動態類型信息。


有人說 Go 的接口是動態類型的,但這是一種誤導。它們是靜態類型的:接口類型的變量總是具有相同的靜態類型,即使在運行時接口變量中存儲的值可能改變類型,該值將始終滿足接口。


通常,如果要指示類型nil的值interface,請使用顯式nil值,然后可以測試是否nil相等。最常見的例子是內置error類型,它是一種方法的接口。只要沒有錯誤,您就明確設置或返回值,nil而不是某些具體(非接口)類型錯誤變量的值(這將是非常糟糕的做法,請參見下面的演示)。


在您的示例中,混淆源于以下事實:


您希望將值作為接口類型 ( shower)

但是您要存儲在切片中的值不是類型shower而是具體類型

因此,當您將*display類型放入shower切片時,將創建一個接口值,它是一對 (value;type) 其中 value isnil和 type is *display。該對中的值將是nil,而不是接口值本身。如果您將一個nil值放入切片中,則接口值本身將是nil,條件x == nil將是true。


示范


請參閱此示例:游樂場


type MyErr string


func (m MyErr) Error() string {

    return "big fail"

}


func doSomething(i int) error {

    switch i {

    default:

        return nil // == nil

    case 1:

        var p *MyErr

        return p // != nil

    case 2:

        return (*MyErr)(nil) // != nil

    case 3:

        var p *MyErr

        return error(p) // != nil because the interface points to a

                        // nil item but is not nil itself.

    case 4:

        var err error // == nil: zero value is nil for the interface

        return err    // This will be true because err is already interface type

    }

}


func main() {

    for i := 0; i <= 4; i++ {

        err := doSomething(i)

        fmt.Println(i, err, err == nil)

    }

}

輸出:


0 <nil> true

1 <nil> false

2 <nil> false

3 <nil> false

4 <nil> true

在第 2 種情況下,nil返回一個指針,但首先將其轉換為接口類型 ( error),因此會創建一個接口值,該nil值包含一個值和類型*MyErr,因此接口值不是nil。


查看完整回答
反對 回復 2021-09-21
?
qq_笑_17

TA貢獻1818條經驗 獲得超7個贊

讓我們把接口想象成一個指針。


假設你有一個指針a,它是零,指向什么。


var a *int // nil

然后你有一個指針b,它指向a.


var b **int

b = &a // not nil

看看發生了什么?b指向一個不指向任何內容的指針。因此,即使它是鏈末端的 nil 指針,b也確實指向某些東西 - 它不是 nil。


如果您查看進程的內存,它可能如下所示:


address | name | value

1000000 | a    | 0

2000000 | b    | 1000000

看?a指向地址 0(這意味著它是nil),并b指向a(1000000)的地址。


這同樣適用于接口(除了它們在內存中看起來有點不同)。


像指針一樣,指向 nil 指針的接口本身不會是 nil。



查看完整回答
反對 回復 2021-09-21
?
小怪獸愛吃肉

TA貢獻1852條經驗 獲得超1個贊

通過提供您正在尋找的確切答案,我將采用另一種方式來回答您的具體問題:


更換支票:


if x == nil {

   panic("everything ok, nil found")

}

和:


if _, ok := x.(display); !ok {

   panic("everything ok, nil found")

}

這里的想法是我們試圖將界面類型(淋?。┺D換為具體類型的顯示。顯然第二個切片項目(d.SubDisplay)不是。


查看完整回答
反對 回復 2021-09-21
  • 3 回答
  • 0 關注
  • 231 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號