Heim  >  Artikel  >  Backend-Entwicklung  >  Wie instanziiere ich mit einem generischen Go einen Zeiger ungleich Null auf einen Typparameter?

Wie instanziiere ich mit einem generischen Go einen Zeiger ungleich Null auf einen Typparameter?

WBOY
WBOYnach vorne
2024-02-11 17:36:11397Durchsuche

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

php-Editor Xigua zeigt Ihnen, wie Sie Nicht-Null-Zeiger für generische Instanziierungstypparameter in der Go-Sprache verwenden. In der Go-Sprache sind Generika eine leistungsstarke Funktion, die die Flexibilität und Wiederverwendbarkeit von Code erhöhen kann. Wenn wir in einer generischen Funktion oder Methode einen Zeiger ungleich Null instanziieren müssen, können wir dies mithilfe von Typzusicherungen und -reflexionen erreichen. Mithilfe dieser Techniken können wir zur Laufzeit eine Zeigerinstanz ungleich Null basierend auf dem spezifischen Typ des Typparameters erstellen und so die Flexibilität und Vielseitigkeit von Generika erreichen. Schauen wir uns die spezifische Implementierungsmethode genauer an.

Frageninhalt

Da nun Typparameter auf golang/go:master verfügbar sind, habe ich beschlossen, es auszuprobieren. Es scheint, dass ich auf eine Einschränkung gestoßen bin, die ich im Vorschlag für Typparameter nicht finden kann. (Oder ich muss es verpasst haben).

Ich möchte eine Funktion schreiben, die einen Ausschnitt generischer Typwerte mit Schnittstellentypbeschränkungen zurückgibt. Wenn der übergebene Typ eine Implementierung mit einem Zeigerempfänger ist, wie instanziieren wir ihn?

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
}

Bei Verwendung von *Count 类型作为 T 调用上述 SetGetterSlice() 函数时,此代码将在调用 Set(v) 时出现混乱。 (Go2go 游乐场)毫不奇怪,因为基本上代码创建了 nil Zeigerscheiben:

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

Variation derselben Frage

Diese Idee funktioniert nicht, ich kann anscheinend keinen einfachen Weg finden, den angegebenen Wert zu instanziieren.

  1. out[i] = new(T) 将导致编译失败,因为它返回 *T,其中类型检查器希望查看 T.
  2. Anruf *new(T) 进行编译,但会导致相同的运行时恐慌,因为 new(T) 返回 **Count 在这种情况下,其中指向 Count 的指针仍然是 nil.
  3. Das Ändern des Rückgabetyps in ein Zeigerfragment auf T führt dazu, dass die Kompilierung fehlschlägt:
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
}

Lösung

Die einzige Lösung, die ich bisher gefunden habe, besteht darin, die Übergabe des Konstruktors an eine generische Funktion zu verlangen. Aber es fühlt sich falsch und ein bisschen langweilig an. Wenn func F(T interface{})() []T eine vollkommen gültige Syntax ist, warum wird sie dann benötigt?

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

Zusammenfassung

Meine Fragen (in der Reihenfolge ihrer Priorität):

  1. Übersehe ich etwas Offensichtliches?
  2. Ist das eine Einschränkung von Generika in Go? Ist das so gut, wie es nur geht?
  3. Ist diese Einschränkung bekannt oder sollte ich ein Problem im Go-Projekt ansprechen?

Workaround

Grundsätzlich müssen Sie der Einschränkung einen Typparameter hinzufügen, um T in seinen Zeigertyp konvertierbar zu machen. In ihrer einfachsten Form sieht die Technik so aus (mit Einschränkungen der Anonymität):

func Foo[T any, PT interface { *T; M() }]() {
    p := PT(new(T))
    p.M() // calling method on non-nil pointer
}

Spielplatz:https://www.php.cn/link/24aef8cb3281a2422a59b51659f1ad2e

Schritt-für-Schritt-Lösung

Ihre Einschränkungen SetGetter 已经声明了类型参数 V, daher modifizieren wir das obige Beispiel leicht:

// V is your original type param
// T is the additional helper param
type SetGetter[V any, T any] interface {
    Set(V)
    Get() V
    *T
}

Dann definieren Sie SetGetterSlice 函数,其类型参数为 T any,其目的只是实例化约束 SetGetter.

Sie können dann den Ausdruck &out[i] in einen Zeigertyp umwandeln und die Methode erfolgreich auf dem Zeigerempfänger aufrufen:

// 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
}

Volles Programm:

CFE57E536C89530D9A8C38E10967A10D

Dies wird ausführlicher, da SetGetterSlice jetzt drei Typparameter erfordert: das ursprüngliche V plus SetGetterSlice 现在需要三个类型参数:原始 V 加上 T (带有指针接收器的类型)和 PT (新约束)。然而,当您调用该函数时,您可以省略第三个 - 通过类型推断,实例化 PT SetGetter[V,T] 所需的类型参数 VT (der Typ mit dem Zeigerempfänger) und PT ( neue Einschränkung). Wenn Sie die Funktion jedoch aufrufen, können Sie den dritten weglassen – durch Typinferenz die Typparameter <code>V, die zum Instanziieren von PT SetGetter[V,T] erforderlich sind und

Das ist alles bekannt:

SetGetterSlice[int, Count](ints)
Spielplatz:https://www.php.cn/link/6b061fc28f7473418a006dfa832708b1

🎜

Das obige ist der detaillierte Inhalt vonWie instanziiere ich mit einem generischen Go einen Zeiger ungleich Null auf einen Typparameter?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen