Menu

Golang Struct: definieren, initialisieren, vergleichen, taggen

Structs fassen benannte Felder zu einem Typ zusammen. So definierst und initialisierst du sie, Nullwerte, Pointer auf Structs, anonyme Structs, Vergleiche, Struct Tags und die Konvention der NewX-Konstruktoren.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Ein Struct definieren und erzeugen

Ein Struct-Typ ist eine Liste benannter Felder. Du deklarierst ihn mit type Name struct { ... } und erzeugst Werte mit einem Composite Literal.

Ausgabe:

Ana 31
{Name:Ana Email:ana@example.com Age:32 Admin:false}
{Name: Email: Age:0 Admin:false}
main.User{Name:"Bob", Email:"", Age:0, Admin:false}

%+v gibt Feldnamen aus, %#v Go-Syntax. Beides ist der schnellste Weg, ein Struct beim Debuggen anzusehen.

Felder desselben Typs können sich eine Zeile teilen: X, Y float64. Großgeschriebene Feldnamen sind exportiert (sichtbar für andere Pakete und für encoding/json), kleingeschriebene nicht.

Literal-Formen

User{Name: "Ana", Age: 31}        // named fields: order-free, omitted fields are zero
User{"Ana", "a@x.com", 31, false} // positional: every field, in order
&User{Name: "Ana"}                // pointer to a new struct
new(User)                         // pointer to a zeroed struct

Nimm lieber benannte Felder. Positionelle Literale brechen, wenn jemand ein Feld hinzufügt oder umsortiert, und go vet warnt bei Struct-Typen aus anderen Paketen davor (zum Beispiel net/url.Error struct literal uses unkeyed fields). Für winzige lokale Typen wie Point{1, 2} ist die positionelle Form in Ordnung.

Keine Standardwerte: Nullwerte und Konstruktoren

Go hat weder Standardwerte für Felder noch Konstruktoren in der Sprache. Jedes Feld beginnt mit dem Nullwert seines Typs: 0, "", false, nil. Zwei Idiome schließen die Lücke.

Mach den Nullwert brauchbar. sync.Mutex, bytes.Buffer und strings.Builder funktionieren alle ohne Initialisierung. Wenn du einen Typ so entwerfen kannst, tu es.

Schreib eine NewX-Funktion, wenn ein Feld einen Standardwert ungleich null braucht oder etwas angelegt werden muss, etwa eine Map:

NewServer gibt einen Pointer zurück, weil der Server Methoden hat, die ihn ändern, und geteilt wird. Das Struct als Wert zurückzugeben ist bei kleinen unveränderlichen Typen ebenfalls verbreitet (time.Date gibt ein time.Time zurück).

Pointer auf Structs

Beim Feldzugriff dereferenziert Go Struct-Pointer automatisch. Mit p := &User{} schreibst du p.Name, nicht (*p).Name.

Ausgabe:

{11 12}
{11 12} {0 12}
{11 0}

Structs sind Werte. Zuweisungen und Funktionsargumente kopieren sie; ein Pointer teilt eins. Kopien Feld für Feld sind flach: Ein Slice- oder Map-Feld in der Kopie zeigt weiterhin auf dieselben Daten. Wann du was nimmst, steht auf der Seite zu Pointern.

Anonyme Structs

Ein Struct-Typ lässt sich inline ohne Namen schreiben. Das ist nützlich für einmalige Gruppierungen, tabellengesteuerte Tests und JSON, das du nur einmal dekodierst und liest.

Die Tabelle []struct{ in string; want int } ist die Standardform eines Go-Tests.

Structs vergleichen

Structs lassen sich mit == vergleichen, wenn jedes Feld vergleichbar ist. Gleichheit bedeutet, dass alle Felder gleich sind. Vergleichbare Structs können auch Map-Schlüssel sein.

Für ein Struct mit Slice- oder Map-Feldern schreibst du eine Equal-Methode oder vergleichst in Tests mit reflect.DeepEqual.

Struct Tags

Ein Tag ist ein String-Literal hinter dem Typ eines Felds. Die Sprache ignoriert es; Bibliotheken lesen es per Reflection. Der häufigste Leser ist encoding/json:

Das gibt {"id":7,"name":"Mug"} aus: Der Tag benennt Felder um, omitempty lässt den Nullwert von Price weg, und das nicht exportierte Feld ist für den Encoder unsichtbar. Tags folgen der Konvention key:"value", mehrere Schlüssel werden durch Leerzeichen getrennt: `json:"name" db:"user_name"`. Ein fehlerhafter Tag kompiliert problemlos und wird still ignoriert, was go vet bemerkt. Die Seite zu JSON behandelt die Optionen.

Leere Structs und Reihenfolge der Felder

struct{} hat keine Felder und belegt null Bytes. Es dient als Wert in Sets (map[string]struct{}) und als Signal auf Channels (chan struct{}), wo nur das Ereignis zählt.

Die Reihenfolge der Felder beeinflusst das Speicherlayout: Der Compiler füllt Felder bis zu ihrem Alignment auf. Ein Struct aus bool, int64, bool belegt auf 64-Bit-Systemen 24 Bytes, eins aus int64, bool, bool nur 16. Das zählt nur bei Typen, die in großer Zahl gespeichert werden.

Häufige Fehler

  • Eine Kopie ändern. for _, u := range users { u.Age++ } ändert jedes Mal eine Kopie. Nimm den Index: users[i].Age++.
  • Das Initialisieren eines Map-Felds vergessen. Der Nullwert des Structs enthält eine nil-Map; das Schreiben darauf löst eine Panic aus. Leg sie in einem Konstruktor an.
  • Ein Struct mit einem sync.Mutex kopieren. Die Kopie hat ihren eigenen Lock-Zustand. Übergib solche Structs per Pointer; go vet meldet die Kopie.
  • Positionelle Literale für Typen anderer Pakete. Sie brechen, wenn ein Feld hinzukommt.

Häufig gestellte Fragen

Wie initialisiert man ein Struct in Go?

Mit einem Composite Literal. Benannte Felder sind die übliche Form: u := User{Name: "Ana", Age: 31}, und jedes Feld, das du weglässt, bekommt seinen Nullwert. User{} ergibt lauter Nullwerte, &User{...} einen Pointer und new(User) einen Pointer auf ein genulltes Struct.

Unterstützt Go Standardwerte für Struct-Felder?

Nein. Jedes Feld beginnt mit dem Nullwert seines Typs. Das Idiom ist eine Konstruktorfunktion, func NewServer() *Server { return &Server{Port: 8080} }, oder den Typ so zu entwerfen, dass schon sein Nullwert brauchbar ist, wie bei sync.Mutex und bytes.Buffer.

Wie vergleicht man zwei Structs in Go?

Mit ==, wenn jedes Feld vergleichbar ist. Zwei Struct-Werte sind gleich, wenn alle einander entsprechenden Felder gleich sind. Ein Struct mit einem Slice-, Map- oder Funktionsfeld lässt sich nicht mit == vergleichen (Compilerfehler); vergleiche die Felder selbst oder nimm in Tests reflect.DeepEqual.

Was sind Struct Tags in Go?

String-Literale hinter dem Typ eines Felds, etwa `json:"name,omitempty"`. Der Compiler ignoriert sie; Pakete wie encoding/json, Datenbanktreiber und Validatoren lesen sie per Reflection, um zu steuern, wie Felder kodiert, benannt oder geprüft werden.

Wie gebe ich in Go ein Struct mit Feldnamen aus?

Mit fmt.Printf("%+v\n", s), das {Name:Ana Age:31} ausgibt. %v gibt nur die Werte aus, und %#v gibt Go-Syntax samt Typnamen aus: main.User{Name:"Ana", Age:31}.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S