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

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

不能使用 *T 類型的變量作為參數的類型

不能使用 *T 類型的變量作為參數的類型

Go
MMTTMM 2022-11-28 14:42:29
我正在學習 Go 1.18 泛型,我試圖理解為什么我在這里遇到麻煩。長話短說,我正在嘗試Unmarshal一個 protobuf,我希望參數類型blah“正常工作”。我已經盡可能地簡化了問題,并且這個特定的代碼正在重現我看到的相同錯誤消息:./prog.go:31:5: cannot use t (variable of type *T) as type stringer in argument to do:    *T does not implement stringer (type *T is pointer to type parameter, not type parameter)package mainimport "fmt"type stringer interface {    a() string}type foo struct{}func (f *foo) a() string {    return "foo"}type bar struct{}func (b *bar) a() string {    return "bar"}type FooBar interface {    foo | bar}func do(s stringer) {    fmt.Println(s.a())}func blah[T FooBar]() {    t := &T{}    do(t)}func main() {    blah[foo]()}我意識到我可以通過不使用泛型(即,將實例傳遞給 )來完全簡化此示例blah(s stringer) {do(s)}。但是,我確實想了解為什么會發生錯誤。我需要用這段代碼更改什么,以便我可以創建一個實例T并將該指針傳遞給一個需要特定方法簽名的函數?
查看完整描述

1 回答

?
偶然的你

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

在您的代碼中,約束FooBar和stringer. 此外,這些方法是在指針接收器上實現的。


對你設計的程序的一個快速而骯臟的修復是簡單地斷言它*T確實是一個stringer:


func blah[T FooBar]() {

    t := new(T)

    do(any(t).(stringer))

}

游樂場:https ://go.dev/play/p/zmVX56T9LZx


但這放棄了類型安全,并可能在運行時出現恐慌。為了保持編譯時類型安全,另一種在某種程度上保留程序語義的解決方案是:


type FooBar[T foo | bar] interface {

    *T

    stringer

}


func blah[T foo | bar, U FooBar[T]]() {

    var t T

    do(U(&t))

}

那么這是怎么回事?


首先,類型參數與其約束之間的關系不是同一性:T 不是 FooBar。您不能T像以前那樣使用FooBar,因此*T絕對不等同于*fooor *bar。


因此,當您調用 時do(t),您試圖將一個類型*T傳遞給需要stringer,但是,指針或不是指針的東西,只是在其類型集中T沒有固有的方法。a() string


第 1 步:將方法添加a() string到FooBar接口中(通過嵌入stringer):


type FooBar interface {

    foo | bar

    stringer

}

但這還不夠,因為現在您的類型都沒有實際實現它。兩者都在指針接收器上聲明了方法。


第 2 步:將聯合中的類型更改為指針:


type FooBar interface {

    *foo | *bar

    stringer

}

此約束現在有效,但您還有另一個問題。當約束沒有核心類型時,您不能聲明復合文字。所以t := T{}也是無效的。我們將其更改為:


func blah[T FooBar]() {

    var t T // already pointer type

    do(t)

}

現在可以編譯了,但t實際上是指針類型的零值,所以它是nil. 您的程序不會崩潰,因為這些方法只返回一些字符串文字。


如果您還需要初始化指針引用的內存,則blah需要了解基本類型。


第 3 步:因此,您添加T foo | baras one type 參數,并將簽名更改為:


func blah[T foo | bar, U FooBar]() {

    var t T

    do(U(&t))

}

完畢?還沒有。轉換U(&t)仍然無效,因為兩者的類型集U不T匹配。您現在需要FooBar在T.


第 4 步:基本上,您將 的聯合提取FooBar到一個類型參數中,這樣在編譯時它的類型集將僅包括以下兩種類型之一:


type FooBar[T foo | bar] interface {

    *T

    stringer

}

現在可以使用 實例化約束T foo | bar,保留類型安全、指針語義并初始化T為非零。


func (f *foo) a() string {

    fmt.Println("foo nil:", f == nil)

    return "foo"

}


func main() {

    blah[foo]()

}

印刷:


foo nil: false

foo

游樂場:https ://go.dev/play/p/src2sDSwe5H


如果你可以blah用指針類型實例化,或者更好地向它傳遞參數,你就可以刪除所有的中間技巧:


type FooBar interface {

    *foo | *bar

    stringer

}


func blah[T FooBar](t T) {

    do(t)

}


func main() {

    blah(&foo{})

}


查看完整回答
反對 回復 2022-11-28
  • 1 回答
  • 0 關注
  • 158 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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