ProgrammierungBackend-Entwickler

Was ist eine leere Struktur struct{} in Go und wann soll man sie verwenden? Welche Möglichkeiten und Feinheiten gibt es dabei, insbesondere im Zusammenhang mit der Speicheroptimierung und der Implementierung von Mengen (set) oder Kanälen für Signale?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

In Go ist der Typ struct{} eine Struktur ohne Felder und benötigt 0 Bytes Speicher. Dies ist eine wertvolle Eigenschaft, die genutzt wird, um den Ressourcenverbrauch zu minimieren, wenn das Vorhandensein von etwas wichtig ist, aber keine Daten benötigt werden — zum Beispiel bei der Implementierung von Mengen (set), Signalstrukturen oder Ereigniskanälen.

Beispiel zur Verwendung für Mengen:

mySet := make(map[string]struct{}) mySet["foo"] = struct{}{} if _, ok := mySet["foo"]; ok { fmt.Println("foo ist in mySet vorhanden") }
  • struct{} wird als Wert verwendet, um die Anwesenheit eines Schlüssels anzuzeigen.
  • Im Gegensatz zu map[string]bool ist die Variante mit struct{} speichereffizienter.

Kanäle für Signale:

done := make(chan struct{}) // Die Goroutine wartet auf das Abschluss-Signal <-done
  • Über diesen Kanal wird einfach ein Ereignis gesendet, keine Daten.

Fangfrage

Was ist der Unterschied zwischen map[string]struct{} und map[string]bool bei der Implementierung von Mengen? Warum ist map[string]struct{} vorzuziehen und hat es Nachteile?

Antwort:

  • map[string]struct{} verwendet 0 Bytes für den Wert, die kompakteste Variante — spart Speicher, besonders bei großen Datenmengen.
  • map[string]bool benötigt 1 Byte für den Wert (intern kann es aufgrund der Ausrichtung dennoch mehr Speicher verbrauchen).
  • In beiden Varianten ist die Logik identisch — der Wert spielt keine Rolle, nur das Vorhandensein des Schlüssels.
  • Nachteil von map[string]struct{}: Es ist nicht einfach, eine Liste aller als true markierten Werte zu erhalten, falls ein boolescher Wert benötigt wird. Es muss trotzdem über die Schlüssel iteriert werden.

Beispiel:

set := make(map[string]struct{}) set["Alice"] = struct{}{} _, exists := set["Alice"] // true flags := make(map[string]bool) flags["Alice"] = true _, exists := flags["Alice"] // true

Beispiele für reale Fehler aufgrund des Unwissens über die Feinheiten des Themas


Geschichte

In einem Projekt zur Speicherung einzigartiger Identifikatoren wurde map[string]bool verwendet, was mit zunehmenden Daten zu einem erheblichen Anstieg des Speicherbedarfs führte — Hundert Megabyte im Vergleich zur Variante mit map[string]struct{}. Der Umstieg auf struct{} reduzierte den Speicherbedarf um mehr als das Doppelte.


Geschichte

Ein Anfänger signalisierte den Abschluss einer Goroutine über chan bool. Bei einer großen Anzahl von Goroutinen erwies sich die Übertragung des Wertes als überflüssig: Es wurde nirgendwo analysiert, ob true oder false gesendet wurde. Der Umstieg auf chan struct{} zeigte eine architektonische Ungenauigkeit und vereinfachte den Code.


Geschichte

In einer Bibliothek wurde eine Menge über map implementiert, bei der als Wert string verwendet wurde. Dies benötigte zu viel Speicher. Nach einer Code-Überprüfung wurde auf map[typ]struct{} umgestellt. Der Fehler wurde nach der Analyse des Speicherprofils bei Lasttests festgestellt, als die Anwendung aufgrund von OOM abstürzte.