php小編西瓜將為您介紹如何在Go語言中使用泛型實例化類型參數(shù)的非零指針。在Go語言中,泛型是一種強大的特性,可以增加代碼的靈活性和重用性。當我們需要在泛型函數(shù)或方法中實例化一個非零指針時,可以使用類型斷言和反射來實現(xiàn)。通過使用這些技術,我們可以在運行時根據(jù)類型參數(shù)的具體類型來創(chuàng)建一個非零指針實例,從而實現(xiàn)泛型的靈活性和通用性。下面我們來詳細了解一下具體的實現(xiàn)方法。
問題內容
現(xiàn)在 golang/go:master
上提供了類型參數(shù),我決定嘗試一下。看來我遇到了在類型參數(shù)提案中找不到的限制。 (或者我一定錯過了)。
我想編寫一個函數(shù),它返回帶有接口類型約束的泛型類型值的切片。如果傳遞的類型是帶有指針接收器的實現(xiàn),我們如何實例化它?
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()
函數(shù)時,此代碼將在調用 Set(v)
時出現(xiàn)混亂。 (Go2go 游樂場)毫不奇怪,因為基本上代碼創(chuàng)建了 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 }
登錄后復制
解決方法
到目前為止我發(fā)現(xiàn)的唯一解決方案是要求將構造函數(shù)傳遞給泛型函數(shù)。但這感覺不對,而且有點乏味。如果 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) }) }
登錄后復制
摘要
我的問題(按優(yōu)先順序排列):
-
我是否忽略了一些顯而易見的事情?
這是 Go 中泛型的限制嗎?這已經(jīng)是最好的了嗎?
此限制是否已知,或者我應該在 Go 項目中提出問題嗎?
解決方法
基本上,您必須向約束添加一個類型參數(shù),以使 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
已經(jīng)聲明了類型參數(shù) 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
函數(shù),其類型參數(shù)為 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
現(xiàn)在需要三個類型參數(shù):原始 V
加上 T
(帶有指針接收器的類型)和 PT
(新約束)。然而,當您調用該函數(shù)時,您可以省略第三個 – 通過類型推斷,實例化 PT SetGetter[V,T]
所需的類型參數(shù) V
和 T
都是已知的:
SetGetterSlice[int, Count](ints)
登錄后復制
游樂場:https://www.php.cn/link/6b061fc28f7473418a006dfa832708b1