Typ-Parameter in Go 1.18

Typdeklarationen und Funktionsdefinitionen können jetzt mit Typ-Parametern ergänzt werden. Interfaces werden als Constraints eingesetzt, um die generischen Parameter einzuschränken. Dabei wird, ähnlich den normalen Funktionsparametern, eine Liste mit Namen und Constraints angegeben.

Stellenmarkt
  1. Entwicklungsingenieur Systemtest (m/w/d)
    Knorr-Bremse Systeme für Nutzfahrzeuge GmbH Schwieberdingen, Schwieberdingen bei Stuttgart
  2. Wissenschaftliche Mitarbeiterin / Wissenschaftlicher Mitarbeiter (m/w/d) am Lehrstuhl für ... (m/w/d)
    Universität Passau, Passau
Detailsuche

Die Typ-Parameter werden in eckigen Klammern nach dem Typen- oder Funktionsnamen angegeben.

  1. func MeineFunktion[T any](slice []T) T { return slice[0] }
  2.  
  3. type MeinStruct[T comparable] struct { Attribut T }
  4.  
  5. type MeinInterface[T constraints.Ordered] interface { Sort([]T) }

Wird ein Type Parameter angegeben, kann er in der Parameterliste (oder den Attributen) verwendet werden. Selbst Interfaces unterstützen die Type Parameter, was eine Kombination von beiden Konzepten zulässt.

Golem Karrierewelt
  1. C++ Programmierung Basics: virtueller Fünf-Tage-Workshop
    20.-24.06.2022, virtuell
  2. Cloud Competence Center: Strategien, Roadmap, Governance: virtueller Ein-Tages-Workshop
    26.07.2022, Virtuell
Weitere IT-Trainings

Um das Prinzip im Detail zu verdeutlichen, betrachten wir zunächst die einfache Funktion Max, die uns die größere zweier Zahlen zurückgeben soll.

  1. func Max(a, b int32) int32 {
  2. if a < b {
  3. return a
  4. }
  5. return b
  6. }

In diesem Fall wurde die Funktion mit dem Typ int32 implementiert, der eine 32 Bit lange, vorzeichenbehaftete Ganzzahl darstellt. Wollen wir allerdings das größere zweier Bytes berechnen, so müssen wir das Byte entweder konvertieren oder eine spezifische Version für Bytes schreiben.

Go achtet außerdem sehr streng auf die Datentypen. Anders als in C werden selbst kompatible Datentypen nicht automatisch konvertiert. Wollen wir also alle vordefinierten Datentypen abdecken, müssten wir je eine Funktion für int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64 und string schreiben, von den Typen abgesehen, die daraus abgeleitet sind.

Go - Das Praxisbuch: Einstieg in Go und das Go-Ökosystem

Mit Generics lässt sich die Funktion für alle Typen schreiben, für die die Größenvergleiche (also <, <=, > oder >=) definiert sind. Wir benutzen dafür den Typ Ordered aus dem golang.org/x/exp/constraints-Paket. In der Beta war constraints noch ein Teil der Standardbibliothek, findet sich jetzt aber nur noch unter den experimentellen Paketen in golang.org/x. Der Go-Compiler prüft automatisch, ob die verwendeten Operatoren auch wirklich bei allen Typen zur Verfügung stehen, die der Constraint zulässt. Hätten wir any als Constraint verwendet (also keine Einschränkungen), hätte der Compiler einen Fehler ausgegeben.

  1. func Max[T constraints.Ordered](a, b T) T {
  2. if a > b {
  3. return a
  4. }
  5. return b
  6. }

Fertig. Alle vierzehn Funktionen in einer. Beim Aufruf der Funktion muss jetzt allerdings der konkrete Typ mit übergeben werden.

  1. m := Max[byte](100, 200)

Go ermöglicht es uns die Angabe auszulassen, wenn alle Datenypen durch die Parameter bereits feststehen. Hier kommt uns die strenge Typisierung zugute.

  1. var a, b byte = 100, 200
  2. m := Max(a, b)

Durch die Constraints ist die Auswahl der Typen eingeschränkt. Der Versuch, das Maximum zweier Slices zu berechnen, würde vom Compiler mit einem Fehler quittiert.

Einschränkungen

Um genauer zu verstehen, wie Go die Constraints anwendet, lohnt sich ein Blick ins Constraints-Paket. Signed ist wie folgt definiert.

  1. type Signed interface {
  2. ~int | ~int8 | ~int16 | ~int32 | ~int64
  3. }

Zunächst sieht der Code einer normalen Interface-Deklaration sehr ähnlich. Statt der Methoden werden aber Typen aufgezählt. Durch die Pipes | werden mehrere alternative Typen definiert. Der Typ-Parameter dürfte in diesem Fall also ein int, int8, int16, int32 oder int64 sein. Die Tilde bedeutet, dass nicht nur der konkrete Typ, sondern auch alle abgeleiteten Datentypen gemeint sind. Neben einem int wäre auch ein type MyInt int gültig.

Wie bei klassischen Interfaces können auch für die Constraints mehrere bestehende Interfaces kombiniert werden. Die große Einschränkung ist allerdings, dass entweder Methoden oder Datentypen, aber keine Mischung aus beiden benutzt werden können.

  1. type Mixed interface {
  2. ~string | String() string // nicht erlaubt
  3. }

Mixed würde zu einem Fehler führen, das Go-Team hält sich aber eine zukünftige Unterstützung offen. Was allerdings geht, und davon macht das constraints-Paket regen Gebrauch, ist die Kombination mehrerer Typ-Interfaces. So werden die Integer durch die Verkettung von Signed und Unsigned definiert.

  1. type Integer interface {
  2. Signed | Unsigned
  3. }

Eine Mischung von konkreten Typen und Typ-Interfaces ist ebenfalls kein Problem. Das Ordered-Interface vereint die Integer und Float Typen mit strings, die in Go ebenfalls die Vergleichsoperationen implementieren.

  1. type Ordered interface {
  2. Integer | Float | ~string
  3. }

Es gibt eine Art von Constraint, der nicht durch die neue Constraints-Syntax abgedeckt wird. Für Typen, die mit == vergleichbar sind, wurde das neue Schlüsselwort comparable eingeführt. Structs, die nur aus vergleichbaren Typen bestehen, implementieren die Gleichheit automatisch, daher ist eine Auflistung der konkreten Typen nicht möglich. Das Wort comparable wird außerdem für Map-Keys benutzt. Nicht zu verwechseln ist comparable übrigens mit dem Vergleich mit nil, also a == nil oder a != nil. Viele Typen unterstützen den Vergleich mit nil, aber nicht mit anderen Variablen gleichen Typs. Für diese Typen gibt es noch keinen Constraint, er könnte aber in einer zukünftigen Version ergänzt werden.

Bitte aktivieren Sie Javascript.
Oder nutzen Sie das Golem-pur-Angebot
und lesen Golem.de
  • ohne Werbung
  • mit ausgeschaltetem Javascript
  • mit RSS-Volltext-Feed
 Go 1.18: Go wird generischGo 1.18: Unser eigener Container 
  1.  
  2. 1
  3. 2
  4. 3
  5.  


Keep The Focus 18. Mär 2022

magst du mal ein Beispiel geben?

Das... 17. Mär 2022

Das ein Extrem ist nicht wirklich besser als ein anderes ist, ist glaub jedem klar.



Aktuell auf der Startseite von Golem.de
Prehistoric Planet
Danke, Apple, für so grandiose Dinosaurier!

Musik von Hans Zimmer, dazu David Attenborough als Sprecher: Apples Prehistoric Planet hat einen Kindheitstraum zum Leben erweckt.
Ein IMHO von Marc Sauter

Prehistoric Planet: Danke, Apple, für so grandiose Dinosaurier!
Artikel
  1. Star Wars: Cal Kestis kämpft in Jedi Survivor weiter
    Star Wars
    Cal Kestis kämpft in Jedi Survivor weiter

    EA hat offiziell den Nachfolger zu Star Wars Jedi Fallen Order angekündigt. Hauptfigur ist erneut Cal Kestis mit seinem Roboterkumpel BD-1.

  2. Fahrgastverband Pro Bahn: Wo das 9-Euro-Ticket sicher gilt
    Fahrgastverband Pro Bahn
    Wo das 9-Euro-Ticket sicher gilt

    Die Farbe der Züge ist entscheidend, was bei der Reiseplanung in der Deutsche-Bahn-App wenig nützt. Dafür laufen Fahrscheinkontrollen ins Leere.

  3. Retro Gaming: Wie man einen Emulator programmiert
    Retro Gaming
    Wie man einen Emulator programmiert

    Warum nicht mal selbst einen Emulator programmieren? Das ist lehrreich und macht Spaß - wenn er funktioniert. Wie es geht, zeigen wir am Gameboy.
    Von Johannes Hiltscher

Du willst dich mit Golem.de beruflich verändern oder weiterbilden?
Zum Stellenmarkt
Zur Akademie
Zum Coaching
  • Schnäppchen, Rabatte und Top-Angebote
    Die besten Deals des Tages
    Daily Deals • PS5 evtl. bestellbar • Prime Video: Filme leihen für 0,99€ • Gigabyte RTX 3080 12GB günstig wie nie: 1.024€ • MSI Gaming-Monitor 32" 4K günstig wie nie: 999€ • Mindstar (u. a. AMD Ryzen 5 5600 179€, Palit RTX 3070 GamingPro 669€) • Days of Play (u. a. PS5-Controller 49,99€) [Werbung]
    •  /