php小編西瓜將為您介紹如何在Go語言中使用泛型實例化類型參數的非零指標。在Go語言中,泛型是一種強大的特性,可以增加程式碼的靈活性和重複使用性。當我們需要在泛型函數或方法中實例化一個非零指標時,可以使用型別斷言和反射來實現。透過使用這些技術,我們可以在運行時根據類型參數的具體類型來建立一個非零指標實例,從而實現泛型的靈活性和通用性。下面我們來詳細了解具體的實作方法。
現在 golang/go:master
上提供了類型參數,我決定嘗試一下。看來我遇到了在類型參數提案中找不到的限制。 (或者我一定錯過了)。
我想寫一個函數,它傳回帶有介面類型約束的泛型類型值的切片。如果傳遞的類型是帶有指標接收器的實現,我們如何實例化它?
type SetGetter[V any] interface { Set(V) Get() V } // SetGetterSlice turns a slice of type V into a slice of type T, // with T.Set() called for each entry in values. func SetGetterSlice[V any, T SetGetter[V]](values []V) []T { out := make([]T, len(values)) for i, v := range values { out[i].Set(v) // panic if T has pointer receiver! } return out }
當使用*Count
類型作為T
呼叫上述SetGetterSlice()
函數時,此程式碼將在呼叫Set(v)
時出現混亂。 (Go2go 遊樂場)毫不奇怪,因為基本上程式碼創建了 nil
指標的切片:
// Count implements SetGetter interface type Count struct { x int } func (c *Count) Set(x int) { c.x = x } func (c *Count) Get() int { return c.x } func main() { ints := []int{1, 2, 3, 4, 5} sgs := SetGetterSlice[int, *Count](ints) for _, s := range sgs { fmt.Println(s.Get()) } }
這個想法行不通,我似乎找不到任何簡單的方法來實例化指向的值。
out[i] = new(T)
將導致編譯失敗,因為它傳回 *T
,其中類型檢查器希望查看 T
。 *new(T)
進行編譯,但會導致相同的執行時間恐慌,因為new(T)
傳回**Count
在這種情況下,其中指向Count
的指標仍然是nil
。 T
的指標片段將導致編譯失敗:func SetGetterSlice[V any, T SetGetter[V]](values []V) []*T { out := make([]*T, len(values)) for i, v := range values { out[i] = new(T) out[i].Set(v) // panic if T has pointer receiver } return out } func main() { ints := []int{1, 2, 3, 4, 5} SetGetterSlice[int, Count](ints) // Count does not satisfy SetGetter[V]: wrong method signature }
到目前為止我發現的唯一解決方案是要求將建構函數傳遞給泛型函數。但這感覺不對,而且有點乏味。如果 func F(T interface{})() []T
是完全有效的語法,為什麼需要這樣做?
func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T { out := make([]T, len(values)) for i, v := range values { out[i] = constructor() out[i].Set(v) } return out } // ... func main() { ints := []int{1, 2, 3, 4, 5} SetGetterSlice[int, *Count](ints, func() *Count { return new(Count) }) }
我的問題(按優先順序排列):
基本上,您必須在約束中新增一個類型參數,以使 T
可轉換為其指標類型。在最基本的形式中,該技術如下所示(帶有匿名約束):
func Foo[T any, PT interface { *T; M() }]() { p := PT(new(T)) p.M() // calling method on non-nil pointer }
遊樂場:https://www.php.cn/link/24aef8cb3281a2422a59b51659f1ad2e
#逐步解決方案
#您的限制 SetGetter
已經宣告了型別參數 V
,因此我們稍微修改上面的範例:
// V is your original type param // T is the additional helper param type SetGetter[V any, T any] interface { Set(V) Get() V *T }
接著定義 SetGetterSlice
函數,其型別參數為 T any
,其目的只是實例化限制 SetGetter
。
然後您就可以將表達式 &out[i]
轉換為指標類型,並成功呼叫指標接收器上的方法:
// T is the type with methods with pointer receiver // PT is the SetGetter constraint with *T func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T { out := make([]T, len(values)) for i, v := range values { // out[i] has type T // &out[i] has type *T // PT constraint includes *T p := PT(&out[i]) // valid conversion! p.Set(v) // calling with non-nil pointer receiver } return out }
完整程式:
CFE57E536C89530D9A8C38E10967A10D這變得更加冗長,因為SetGetterSlice
現在需要三個型別參數:原始V
加上#T
(有指標接收器的型別)和PT
(新限制)。然而,當您呼叫函數時,您可以省略第三個- 透過型別推斷,實例化PT SetGetter[V,T]
所需的型別參數V
和T
都是已知的:
SetGetterSlice[int, Count](ints)
遊樂場:https://www.php.cn/link/6b061fc28f7473418a006dfa832708b1
#以上是如何使用泛型 Go 實例化型別參數的非零指標?的詳細內容。更多資訊請關注PHP中文網其他相關文章!