首頁  >  文章  >  後端開發  >  如何使用泛型 Go 實例化型別參數的非零指標?

如何使用泛型 Go 實例化型別參數的非零指標?

WBOY
WBOY轉載
2024-02-11 17:36:11394瀏覽

如何使用泛型 Go 实例化类型参数的非零指针?

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())
    }
}

同一問題的變體

這個想法行不通,我似乎找不到任何簡單的方法來實例化指向的值。

  1. out[i] = new(T) 將導致編譯失敗,因為它傳回 *T,其中類型檢查器希望查看 T
  2. 呼叫*new(T) 進行編譯,但會導致相同的執行時間恐慌,因為new(T) 傳回**Count 在這種情況下,其中指向Count 的指標仍然是nil
  3. 將傳回類型變更為指向 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) })
}

摘要

我的問題(按優先順序排列):

  1. 我是否忽略了一些顯而易見的事情?
  2. 這是 Go 中泛型的限制嗎?這已經是最好的了嗎?
  3. 此限制是否已知,或者我應該在 Go 專案中提出問題嗎?

解決方法

基本上,您必須在約束中新增一個類型參數,以使 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] 所需的型別參數VT 都是已知的:

SetGetterSlice[int, Count](ints)

遊樂場:https://www.php.cn/link/6b061fc28f7473418a006dfa832708b1

#

以上是如何使用泛型 Go 實例化型別參數的非零指標?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除