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

WBOY
Freigeben: 2024-02-11 17:36:11
nach vorne
379 Leute haben es durchsucht

如何使用泛型 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
}
Nach dem Login kopieren

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())
    }
}
Nach dem Login kopieren

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
}
Nach dem Login kopieren

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) })
}
Nach dem Login kopieren

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
}
Nach dem Login kopieren

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
}
Nach dem Login kopieren

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
}
Nach dem Login kopieren

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< /code> ( 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)
Nach dem Login kopieren
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!

Verwandte Etiketten:
Quelle:stackoverflow.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!