ProgrammierungBackend-Entwickler

Wie unterscheiden sich die Übergabe durch Referenz und durch Wert in Go? Warum ist dieser Punkt kritisch für Strukturen und Slices?

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

Antwort

In Go werden standardmäßig alle Funktionsargumente durch Wert übergeben: Der Wert der Variable wird kopiert. Aber einige Typen (zum Beispiel Slices, Maps, Channels) sind "Wrapper" über internen Strukturen (Zeigern). Die Übergabe eines Slices durch Wert kopiert nur den Deskriptor des Slices, nicht die Daten; beide Variablen verweisen auf dasselbe Array. Im Fall von Strukturen wird die gesamte Struktur kopiert.

Wenn man das Kopieren vermeiden und mit der Originalstruktur arbeiten möchte, verwendet man die Übergabe durch Zeiger (*Struct).

Beispiel:

type User struct { Name string Age int } func updateUser(u User) { u.Age = 30 // ändert nur die Kopie } func updateUserPtr(u *User) { u.Age = 30 // ändert das Original } func main() { u := User{"Ivan", 25} updateUser(u) fmt.Println(u.Age) // 25 updateUserPtr(&u) fmt.Println(u.Age) // 30 }

Fangfrage

Sind Änderungen an einem Slice, das an eine Funktion übergeben wurde, immer außerhalb der Funktion sichtbar?

Nein!

  • Wenn der Inhalt des Slices geändert wird (slice[i] = ...), ist diese Änderung außerhalb sichtbar.
  • Wenn das Slice selbst geändert wird (zum Beispiel slice = append(slice, ...)), und das Ergebnis nicht aus der Funktion zurückgegeben wird — die neuen Elemente werden in einer lokalen Kopie sein, und man wird sie verlieren.

Beispiel:

func addElem(s []int) { s = append(s, 100) } func main() { arr := []int{1,2,3} addElem(arr) fmt.Println(arr) // [1 2 3] — 100 wurde nicht hinzugefügt }

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


Geschichte

In einem der Projekte wurden Datentypen mit einem großen struct-Feld (200+ Bytes) durch Wert über Kanäle zwischen Goroutinen übergeben, was enorme Kopierkosten und Leistungsabfälle verursachte. Nach dem Wechsel zur Übergabe durch Zeiger verringerte sich die Latenz um das Zehnfache.


Geschichte

In einem Auditing-Logging-Service übergab ein Entwickler eine Map zwischen Funktionen, ohne eine explizite Klonierung (copy) vorzunehmen. Änderungen, die von einer Funktion vorgenommen wurden, veränderten unerwartet Daten in einem anderen Teil des Programms, was zu Verwirrung im Log führte.


Geschichte

In einer Funktion zum dynamischen Vergrößern eines Slices innerhalb der Funktion wurde vergessen, das neue Slice zurückzugeben. Infolgedessen wurden die Änderungen nicht im aufrufenden Code reflektiert, was zu einem Verlust von Transaktionen führte. Es wurde beschlossen, das neue Slice aus der Funktion zurückzugeben.