# Go Generics w 2026: Parametry Typów, Ograniczenia i Pytania Rekrutacyjne > Kompleksowy przewodnik po generykach w Go dla programistów przygotowujących się do rozmów kwalifikacyjnych. Parametry typów, ograniczenia, operator tyldy i praktyczne wzorce. - Published: 2026-07-08 - Updated: 2026-07-08 - Author: SharpSkill - Tags: go, generics, interview - Reading time: 9 min --- Go 1.18 wprowadziło generyki do języka w marcu 2022 roku, a od tego czasu funkcjonalność ta dojrzewała przez wersje Go 1.21, 1.22 i kolejne. Ten artykuł przedstawia najważniejsze pytania rekrutacyjne dotyczące generyków w Go na rok 2026, od podstawowych parametrów typów po zaawansowane wzorce ograniczeń, o które często pytają rekruterzy. > **Czym są generyki w Go?** > > Generyki w Go umożliwiają pisanie funkcji i typów, które działają z dowolnym typem danych, zachowując przy tym bezpieczeństwo typów w czasie kompilacji. W przeciwieństwie do interfejsów, które używają asercji typów w czasie wykonania, generyki rozwiązują typy w czasie kompilacji, zapewniając lepszą wydajność i wcześniejsze wykrywanie błędów. ## Zrozumienie parametrów typów w Go Parametry typów stanowią fundament generyków w Go. Parametr typu to symbol zastępczy dla typu, który zostaje określony podczas używania funkcji lub typu generycznego. ```go // generic.go // Basic generic function with type parameter T func PrintSlice[T any](items []T) { for _, item := range items { fmt.Println(item) } } // Usage - type inference determines T automatically func main() { PrintSlice([]int{1, 2, 3}) // T is int PrintSlice([]string{"a", "b"}) // T is string } ``` Składnia nawiasów kwadratowych `[T any]` deklaruje parametr typu `T` ograniczony przez `any`. Kompilator Go automatycznie wywnioskuje konkretny typ z argumentów, eliminując potrzebę jawnego określania typu w większości przypadków. ## Częste pytanie rekrutacyjne: Jaka jest różnica między `any` a `comparable`? Rekruterzy często pytają o wbudowane ograniczenia `any` i `comparable`. Zrozumienie różnic między nimi świadczy o solidnej znajomości generyków. ```go // constraints.go // any: accepts all types (alias for interface{}) func Process[T any](value T) T { return value } // comparable: only types that support == and != func Contains[T comparable](slice []T, target T) bool { for _, v := range slice { if v == target { // This comparison requires comparable return true } } return false } // This compiles Contains([]int{1, 2, 3}, 2) // This fails: slices are not comparable // Contains([][]int{{1}, {2}}, []int{1}) ``` Ograniczenie `comparable` zawęża parametry typów do typów obsługujących operatory równości. Slices, mapy i funkcje są wykluczone, ponieważ Go nie definiuje dla nich równości na poziomie języka. ## Tworzenie własnych ograniczeń typów za pomocą interfejsów Go używa interfejsów do definiowania własnych ograniczeń, rozszerzając możliwości funkcji generycznych. [Specyfikacja Go](https://go.dev/ref/spec#Type_constraints) definiuje sposób działania elementów typów w interfejsach ograniczeń. ```go // number.go // Custom constraint using type union type Number interface { int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64 } // Generic function constrained to numeric types func Sum[T Number](values []T) T { var total T for _, v := range values { total += v // + operator works because all Number types support it } return total } func main() { fmt.Println(Sum([]int{1, 2, 3})) // 6 fmt.Println(Sum([]float64{1.5, 2.5})) // 4.0 } ``` Unie typów z `|` określają, które dokładnie typy spełniają ograniczenie. To podejście zapewnia większą kontrolę niż `any`, unikając jednocześnie asercji typów w czasie wykonania. ## Pytanie rekrutacyjne: Wyjaśnij operator tyldy `~` w ograniczeniach Operator tyldy `~` w ograniczeniach dopasowuje zarówno typ, jak i wszystkie typy mające go jako typ bazowy. To pytanie sprawdza zrozumienie systemu typów w Go. ```go // underlying.go // ~int matches int and any type with int as underlying type type Signed interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 } // Custom type with int as underlying type type UserID int type Temperature int64 func Abs[T Signed](value T) T { if value < 0 { return -value } return value } func main() { var id UserID = -42 var temp Temperature = -10 fmt.Println(Abs(id)) // 42 - works because ~int matches UserID fmt.Println(Abs(temp)) // 10 - works because ~int64 matches Temperature } ``` Bez `~` ograniczenie `int` dopasowałoby tylko dokładny typ `int`, a nie typy niestandardowe jak `UserID`. Tylda rozszerza ograniczenie o typy pochodne, czyniąc kod generyczny bardziej elastycznym. ## Typy generyczne: struktury i metody Typy generyczne wykraczają poza funkcje, obejmując struktury i metody. Ten wzorzec pojawia się często w implementacjach struktur danych. ```go // stack.go // Generic Stack type type Stack[T any] struct { items []T } // Push adds an element to the stack func (s *Stack[T]) Push(item T) { s.items = append(s.items, item) } // Pop removes and returns the top element func (s *Stack[T]) Pop() (T, bool) { if len(s.items) == 0 { var zero T // Zero value for type T return zero, false } index := len(s.items) - 1 item := s.items[index] s.items = s.items[:index] return item, true } func main() { intStack := Stack[int]{} intStack.Push(10) intStack.Push(20) val, ok := intStack.Pop() // val=20, ok=true } ``` Metody na typach generycznych muszą powtarzać parametr typu w nawiasach, ale nie mogą wprowadzać nowych parametrów typów. Odbiorca `(s *Stack[T])` wiąże metodę z konkretną instancją `Stack`. ## Pytanie rekrutacyjne: Dlaczego metody nie mogą mieć własnych parametrów typów? Go zabrania dodatkowych parametrów typów w metodach, co jest decyzją projektową zaskakującą programistów przychodzących z języków takich jak Java czy C#. [Propozycja generyków Go](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md) wyjaśnia, że ograniczenie to istnieje, aby system typów pozostał zrozumiały. ```go // This is NOT valid Go code type Container[T any] struct { value T } // ERROR: methods cannot have type parameters // func (c *Container[T]) Transform[U any](fn func(T) U) U { // return fn(c.value) // } // Valid alternative: use a standalone function func Transform[T, U any](c *Container[T], fn func(T) U) U { return fn(c.value) } ``` Obejściem jest użycie funkcji najwyższego poziomu z wieloma parametrami typów zamiast metod. Ten projekt utrzymuje prostotę dyspozycji metod i unika złożonych interakcji między typami odbiorców a parametrami typów metod. ## Wnioskowanie typów i wnioskowanie ograniczeń Wnioskowanie typów w Go redukuje rozwlekłość kodu przy wywoływaniu funkcji generycznych. Zrozumienie, kiedy wymagane są jawne argumenty typów, pomaga pisać czystszy kod. ```go // inference.go func Map[T, R any](input []T, transform func(T) R) []R { result := make([]R, len(input)) for i, v := range input { result[i] = transform(v) } return result } func main() { numbers := []int{1, 2, 3} // Type inference: T=int, R=string inferred from arguments strings := Map(numbers, func(n int) string { return fmt.Sprintf("%d", n) }) // Explicit types sometimes required for complex cases // Map[int, string](numbers, converter) } ``` Wnioskowanie typów działa od argumentów funkcji do parametrów typów. Gdy kompilator nie może wywnioskować typów samodzielnie z argumentów, jawne argumenty typów w nawiasach kwadratowych rozwiązują niejednoznaczność. ## Pakiet constraints: `cmp` i `slices` w bibliotece standardowej Go 1.21 dodało pakiet `cmp` z ograniczeniem `Ordered` i funkcjami porównawczymi. Pakiet `slices` demonstruje idiomatyczny kod generyczny w bibliotece standardowej. ```go // stdlib.go import ( "cmp" "slices" ) func main() { numbers := []int{3, 1, 4, 1, 5, 9} // slices.Sort uses cmp.Ordered constraint internally slices.Sort(numbers) // [1, 1, 3, 4, 5, 9] // Binary search on sorted slice index, found := slices.BinarySearch(numbers, 4) // cmp.Compare returns -1, 0, or 1 result := cmp.Compare(3, 5) // -1 // cmp.Or returns first non-zero value value := cmp.Or(0, 0, 42, 100) // 42 } ``` [Dokumentacja biblioteki standardowej](https://pkg.go.dev/cmp) pokazuje, jak te pakiety wykorzystują generyki do operacji bezpiecznych typowo na typach uporządkowanych. Znajomość tych pakietów świadczy o praktycznej wiedzy o generykach wykraczającej poza teoretyczne zrozumienie. ## Pytanie rekrutacyjne: Implementacja generycznego cache'a Częstym zadaniem rekrutacyjnym jest implementacja generycznego, bezpiecznego wątkowo cache'a. Sprawdza to znajomość generyków, współbieżności z [pakietem sync](/technologies/go/interview-questions/sync-primitives) oraz projektowania API. ```go // cache.go import ( "sync" "time" ) type Cache[K comparable, V any] struct { mu sync.RWMutex items map[K]cacheItem[V] } type cacheItem[V any] struct { value V expiration time.Time } func NewCache[K comparable, V any]() *Cache[K, V] { return &Cache[K, V]{ items: make(map[K]cacheItem[V]), } } func (c *Cache[K, V]) Set(key K, value V, ttl time.Duration) { c.mu.Lock() defer c.mu.Unlock() c.items[key] = cacheItem[V]{ value: value, expiration: time.Now().Add(ttl), } } func (c *Cache[K, V]) Get(key K) (V, bool) { c.mu.RLock() defer c.mu.RUnlock() item, exists := c.items[key] if !exists || time.Now().After(item.expiration) { var zero V return zero, false } return item.value, true } ``` Ta implementacja używa `comparable` dla kluczy (wymaganie mapy) i `any` dla wartości. Osobna struktura `cacheItem` pokazuje, jak typy generyczne mogą być zagnieżdżone. Bezpieczeństwo wątkowe zapewnia `sync.RWMutex`, wzorzec omawiany w [rozmowach o współbieżności w Go](/technologies/go/interview-questions/concurrency-patterns). ## Wartości zerowe i ograniczenia typów Obsługa wartości zerowych w kodzie generycznym wymaga zrozumienia, jak Go inicjalizuje zmienne typów sparametryzowanych. ```go // zero.go // Return zero value when slice is empty func First[T any](slice []T) T { if len(slice) == 0 { var zero T // Zero value: 0 for int, "" for string, nil for pointers return zero } return slice[0] } // Alternative: return pointer to avoid ambiguity func FirstOrNil[T any](slice []T) *T { if len(slice) == 0 { return nil } return &slice[0] } ``` Wzorzec `var zero T` tworzy wartość zerową dla dowolnego typu `T`. Dla typów, gdzie zero jest prawidłową wartością (jak `0` dla liczb całkowitych), wariant zwracający wskaźnik rozróżnia między "nie znaleziono" a "znaleziono zero". ## Zaawansowane: łączenie wielu ograniczeń Złożone funkcje generyczne mogą wymagać, aby typy spełniały wiele ograniczeń. Go obsługuje to poprzez osadzanie interfejsów. ```go // combined.go // Constraint requiring both ordering and string conversion type Stringable interface { String() string } type OrderedStringable interface { cmp.Ordered Stringable } // Alternative: use type parameters with multiple constraints inline func PrintSorted[T interface{ cmp.Ordered; fmt.Stringer }](items []T) { slices.Sort(items) for _, item := range items { fmt.Println(item.String()) } } ``` Osadzanie interfejsów łączy ograniczenia, wymagając od typów implementacji wszystkich osadzonych interfejsów. Składnia inline `interface{ A; B }` zapewnia tę samą funkcjonalność bez deklarowania nazwanego typu ograniczenia. ## Podsumowanie - Parametry typów z `[T any]` umożliwiają pisanie wielokrotnego użytku, bezpiecznego typowo kodu bez refleksji w czasie wykonania - `comparable` ogranicza do typów obsługujących `==` i `!=`, wymaganych dla kluczy map - Operator tyldy `~` dopasowuje typy z określonym typem bazowym, rozszerzając elastyczność ograniczeń - Własne ograniczenia używają unii typów (`int | string`) do określenia dokładnych dozwolonych typów - Metody nie mogą mieć własnych parametrów typów; alternatywą są funkcje najwyższego poziomu - Pakiety biblioteki standardowej `cmp` i `slices` demonstrują idiomatyczne wzorce generyczne - Wartości zerowe przez `var zero T` obsługują puste lub brakujące dane w funkcjach generycznych - Bezpieczne wątkowo generyczne struktury danych łączą generyki z prymitywami synchronizacji --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/go/go-generics-type-parameters-constraints-interview-questions