programowanieBackend Developer

Opisz cechy pracy z slices w Go: co to jest nil slice, jaka jest różnica między length a capacity, oraz jak prawidłowo zwiększać slice bez wycieków danych lub paniki?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Slice to dynamiczna tablica, podstawowa jednostka strukturalna przy pracy z tablicami w Go. Historycznie, języki programowania oferowały tablice o stałej wielkości lub cięższe struktury. Go zaimplementował slices jako wygodne narzędzie do przetwarzania kolekcji pamięci z automatycznym zarządzaniem jej rozmiarem i możliwością zmiany długości.

Problem polega na prawidłowym zarządzaniu pamięcią, obsłudze przypadków brzegowych (puste, nil, pełne slices) oraz na zrozumieniu różnicy między długością a pojemnością slice.

Rozwiązanie — prawidłowo korzystać z wbudowanych funkcji len(), cap(), oraz operować na slices zgodnie z konwencjami Go.

Przykład kodu:

var a []int // nil slice, len=0, cap=0 b := make([]int, 0) // pusty slice, len=0, cap=0 c := make([]int, 3, 5) // len=3, cap=5 c = append(c, 4, 5, 6) // cap zwiększy się automatycznie

Kluczowe cechy:

  • Nil slice (var s []int) nie przydziela pamięci w ogóle, różni się od pustego slice (make([]int, 0)) tylko wewnętrznie.
  • Length pokazuje aktualną liczbę elementów, capacity — maksymalne bez dodatkowej alokacji.
  • Przy append, jeśli capacity jest niewystarczająca, pod podwoziem tworzona jest nowa, a stare dane pozostają bez zmian.

Pytania z pułapką.

Jakiej długości i pojemności będzie slice po append do nil slice?

Nil slice (var s []int) po append(s, 1) staje się slice o długości 1, pojemności 1 — Go sam przydziela pamięć.

var s []int s = append(s, 42) // s teraz [42], len=1, cap=1

Czy można uzyskać dostęp do pojemności slice równego nil?

Tak, w nil slice obie funkcje — len i cap — zwracają 0. Paniki nie będzie.

var s []int fmt.Println(len(s), cap(s)) // 0 0

Co się stanie przy próbie przypisania elementu przez indeks bez wcześniejszej alokacji pamięci do slice równego nil?

Panika index out of range, ponieważ slice nie ma elementów.

var s []int s[0] = 1 // panic: runtime error: index out of range

Typowe błędy i antywzorce

  • Błędy przy przekazywaniu nil slice lub pustego slice do funkcji, gdzie wymagana jest określona długość.
  • Nadpisanie slice bez uwzględnienia wzrostu capacity oraz nielogicznie napisane pętle z append.
  • Próby przypisania przez indeks zamiast append do dodawania elementów.

Przykład z życia

Negatywny przypadek

Zespół postanowił w serwerze Go wszędzie używać tylko var s []T, oczekując, że to zawsze będzie równoważne z make([]T, 0). W rezultacie niektóre marshalingi jsona zwracały null, a nie [].

Zalety:

  • Mniej alokacji pamięci na starcie.

Wady:

  • Niepoprawne działanie JSON (w rezultacie przychodził null zamiast tablicy).
  • Błędy runtime podczas uzyskiwania dostępu przez indeks.

Pozytywny przypadek

Użycie make([]T, 0, 100) do prealokacji, a następnie tylko append w pętli. W ten sposób minimalizowane są alokacje pamięci i często uzyskuje się lepszą wydajność.

Zalety:

  • Efektywna praca z pamięcią.
  • Brak panik i nieoczekiwanych wartości przy marshalingu.

Wady:

  • Możliwy nadmiar pamięci, jeśli oczekiwana liczba elementów nie zgadza się z zadaną pojemnością.