Обобщённые структуры данных
Stack[T] -- стек
package main
import (
"errors"
"fmt"
)
var ErrEmpty = errors.New("collection is empty")
// Stack is a generic LIFO container
type Stack[T any] struct {
items []T
}
func NewStack[T any](capacity int) *Stack[T] {
return &Stack[T]{items: make([]T, 0, capacity)}
}
func (s *Stack[T]) Push(vals ...T) {
s.items = append(s.items, vals...)
}
func (s *Stack[T]) Pop() (T, error) {
if len(s.items) == 0 {
var zero T
return zero, ErrEmpty
}
last := len(s.items) - 1
val := s.items[last]
s.items = s.items[:last]
return val, nil
}
func (s *Stack[T]) Peek() (T, error) {
if len(s.items) == 0 {
var zero T
return zero, ErrEmpty
}
return s.items[len(s.items)-1], nil
}
func (s *Stack[T]) Len() int { return len(s.items) }
func (s *Stack[T]) Empty() bool { return len(s.items) == 0 }
func main() {
s := NewStack[int](10)
s.Push(1, 2, 3, 4, 5)
for !s.Empty() {
val, _ := s.Pop()
fmt.Printf("%d ", val) // 5 4 3 2 1
}
fmt.Println()
}
Queue[T] -- очередь
package main
import "fmt"
// Queue is a generic FIFO container using ring buffer
type Queue[T any] struct {
items []T
head int
tail int
count int
}
func NewQueue[T any](capacity int) *Queue[T] {
return &Queue[T]{items: make([]T, capacity)}
}
func (q *Queue[T]) Enqueue(val T) {
if q.count == len(q.items) {
q.grow()
}
q.items[q.tail] = val
q.tail = (q.tail + 1) % len(q.items)
q.count++
}
func (q *Queue[T]) Dequeue() (T, bool) {
if q.count == 0 {
var zero T
return zero, false
}
val := q.items[q.head]
var zero T
q.items[q.head] = zero // help GC
q.head = (q.head + 1) % len(q.items)
q.count--
return val, true
}
func (q *Queue[T]) Len() int { return q.count }
func (q *Queue[T]) Empty() bool { return q.count == 0 }
func (q *Queue[T]) grow() {
newCap := len(q.items) * 2
if newCap == 0 {
newCap = 8
}
newItems := make([]T, newCap)
for i := range q.count {
newItems[i] = q.items[(q.head+i)%len(q.items)]
}
q.items = newItems
q.head = 0
q.tail = q.count
}
func main() {
q := NewQueue[string](4)
q.Enqueue("first")
q.Enqueue("second")
q.Enqueue("third")
for !q.Empty() {
val, _ := q.Dequeue()
fmt.Println(val) // first, second, third
}
}
LinkedList[T] -- связанный список
package main
import "fmt"
// Node is a single element in the linked list
type Node[T any] struct {
Value T
Next *Node[T]
}
// LinkedList is a generic singly-linked list
type LinkedList[T any] struct {
head *Node[T]
tail *Node[T]
len int
}
// Append adds element to the end
func (l *LinkedList[T]) Append(val T) {
node := &Node[T]{Value: val}
if l.tail == nil {
l.head = node
l.tail = node
} else {
l.tail.Next = node
l.tail = node
}
l.len++
}
// Prepend adds element to the beginning
func (l *LinkedList[T]) Prepend(val T) {
node := &Node[T]{Value: val, Next: l.head}
l.head = node
if l.tail == nil {
l.tail = node
}
l.len++
}
// ToSlice converts list to slice
func (l *LinkedList[T]) ToSlice() []T {
result := make([]T, 0, l.len)
for n := l.head; n != nil; n = n.Next {
result = append(result, n.Value)
}
return result
}
// ForEach iterates over all elements
func (l *LinkedList[T]) ForEach(fn func(T)) {
for n := l.head; n != nil; n = n.Next {
fn(n.Value)
}
}
func (l *LinkedList[T]) Len() int { return l.len }
func main() {
list := &LinkedList[int]{}
list.Append(1)
list.Append(2)
list.Append(3)
list.Prepend(0)
fmt.Println(list.ToSlice()) // [0 1 2 3]
list.ForEach(func(v int) {
fmt.Printf("%d ", v) // 0 1 2 3
})
fmt.Println()
}
Утилитарные функции
Map, Filter, Reduce
package main
import "fmt"
// Map transforms each element of a slice
func Map[T, U any](s []T, fn func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = fn(v)
}
return result
}
// Filter returns elements matching predicate
func Filter[T any](s []T, fn func(T) bool) []T {
result := make([]T, 0, len(s)/2) // estimate half will match
for _, v := range s {
if fn(v) {
result = append(result, v)
}
}
return result
}
// Reduce accumulates a value over a slice
func Reduce[T, U any](s []T, initial U, fn func(U, T) U) U {
acc := initial
for _, v := range s {
acc = fn(acc, v)
}
return acc
}
// Contains checks if slice contains an element
func Contains[T comparable](s []T, target T) bool {
for _, v := range s {
if v == target {
return true
}
}
return false
}
// Index returns the first index of target, or -1
func Index[T comparable](s []T, target T) int {
for i, v := range s {
if v == target {
return i
}
}
return -1
}
// Unique returns a new slice with duplicates removed
func Unique[T comparable](s []T) []T {
seen := make(map[T]struct{}, len(s))
result := make([]T, 0, len(s))
for _, v := range s {
if _, ok := seen[v]; !ok {
seen[v] = struct{}{}
result = append(result, v)
}
}
return result
}
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Map: double each number
doubled := Map(nums, func(n int) int { return n * 2 })
fmt.Println("Doubled:", doubled)
// Filter: keep evens
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
fmt.Println("Evens:", evens)
// Reduce: sum
sum := Reduce(nums, 0, func(acc, n int) int { return acc + n })
fmt.Println("Sum:", sum) // 55
// Reduce: join strings
words := []string{"Hello", "World", "Go"}
joined := Reduce(words, "", func(acc, s string) string {
if acc == "" {
return s
}
return acc + " " + s
})
fmt.Println("Joined:", joined) // Hello World Go
// Contains
fmt.Println("Contains 5:", Contains(nums, 5)) // true
fmt.Println("Contains 11:", Contains(nums, 11)) // false
// Unique
dupes := []int{1, 2, 2, 3, 3, 3, 4}
fmt.Println("Unique:", Unique(dupes)) // [1 2 3 4]
}
Keys, Values для карт
package main
import (
"fmt"
"sort"
)
// Keys returns all keys of a map
func Keys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
// Values returns all values of a map
func Values[K comparable, V any](m map[K]V) []V {
vals := make([]V, 0, len(m))
for _, v := range m {
vals = append(vals, v)
}
return vals
}
// Invert swaps keys and values
func Invert[K, V comparable](m map[K]V) map[V]K {
result := make(map[V]K, len(m))
for k, v := range m {
result[v] = k
}
return result
}
// Merge combines multiple maps (later maps override earlier)
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
result := make(map[K]V)
for _, m := range maps {
for k, v := range m {
result[k] = v
}
}
return result
}
func main() {
users := map[int]string{
1: "Alice",
2: "Bob",
3: "Charlie",
}
keys := Keys(users)
sort.Ints(keys)
fmt.Println("Keys:", keys) // [1 2 3]
fmt.Println("Values:", Values(users))
inverted := Invert(users)
fmt.Println("Inverted:", inverted) // map[Alice:1 Bob:2 Charlie:3]
defaults := map[string]int{"timeout": 30, "retries": 3}
overrides := map[string]int{"timeout": 60}
merged := Merge(defaults, overrides)
fmt.Println("Merged:", merged) // map[retries:3 timeout:60]
}
Result[T] -- тип результата
Паттерн, вдохновлённый Rust, для работы с результатами, которые могут быть ошибкой:
package main
import (
"errors"
"fmt"
"strconv"
)
// Result represents a value-or-error
type Result[T any] struct {
value T
err error
}
// Ok creates a successful result
func Ok[T any](val T) Result[T] {
return Result[T]{value: val}
}
// Err creates an error result
func Err[T any](err error) Result[T] {
return Result[T]{err: err}
}
// IsOk returns true if result is successful
func (r Result[T]) IsOk() bool {
return r.err == nil
}
// IsErr returns true if result is an error
func (r Result[T]) IsErr() bool {
return r.err != nil
}
// Unwrap returns the value or panics
func (r Result[T]) Unwrap() T {
if r.err != nil {
panic(fmt.Sprintf("unwrap on error result: %v", r.err))
}
return r.value
}
// UnwrapOr returns the value or a default
func (r Result[T]) UnwrapOr(defaultVal T) T {
if r.err != nil {
return defaultVal
}
return r.value
}
// Error returns the error (or nil)
func (r Result[T]) Error() error {
return r.err
}
// Map transforms the value if Ok
func MapResult[T, U any](r Result[T], fn func(T) U) Result[U] {
if r.err != nil {
return Err[U](r.err)
}
return Ok(fn(r.value))
}
// AndThen chains operations that may fail
func AndThen[T, U any](r Result[T], fn func(T) Result[U]) Result[U] {
if r.err != nil {
return Err[U](r.err)
}
return fn(r.value)
}
func main() {
// Parse chain: string → int → validate → double
parseAge := func(s string) Result[int] {
n, err := strconv.Atoi(s)
if err != nil {
return Err[int](fmt.Errorf("invalid age: %w", err))
}
return Ok(n)
}
validate := func(age int) Result[int] {
if age < 0 || age > 150 {
return Err[int](errors.New("age out of range"))
}
return Ok(age)
}
// Success path
result := AndThen(parseAge("25"), validate)
doubled := MapResult(result, func(n int) string {
return fmt.Sprintf("Age doubled: %d", n*2)
})
fmt.Println(doubled.Unwrap()) // Age doubled: 50
// Error path
result2 := AndThen(parseAge("abc"), validate)
fmt.Println(result2.UnwrapOr(0)) // 0
fmt.Println(result2.Error()) // invalid age: strconv.Atoi: ...
// Validation error
result3 := AndThen(parseAge("200"), validate)
fmt.Println(result3.Error()) // age out of range
}
Optional[T] -- опциональный тип
package main
import "fmt"
// Optional represents a value that may or may not exist
type Optional[T any] struct {
value T
valid bool
}
// Some creates a present Optional
func Some[T any](val T) Optional[T] {
return Optional[T]{value: val, valid: true}
}
// None creates an absent Optional
func None[T any]() Optional[T] {
return Optional[T]{}
}
// IsPresent returns true if value exists
func (o Optional[T]) IsPresent() bool {
return o.valid
}
// Get returns the value or false
func (o Optional[T]) Get() (T, bool) {
return o.value, o.valid
}
// OrElse returns the value or a default
func (o Optional[T]) OrElse(defaultVal T) T {
if o.valid {
return o.value
}
return defaultVal
}
// MapOptional transforms the value if present
func MapOptional[T, U any](o Optional[T], fn func(T) U) Optional[U] {
if !o.valid {
return None[U]()
}
return Some(fn(o.value))
}
// FlatMap chains operations that return Optional
func FlatMap[T, U any](o Optional[T], fn func(T) Optional[U]) Optional[U] {
if !o.valid {
return None[U]()
}
return fn(o.value)
}
func main() {
// Find user by ID
findUser := func(id int) Optional[string] {
users := map[int]string{1: "Alice", 2: "Bob"}
if name, ok := users[id]; ok {
return Some(name)
}
return None[string]()
}
// Success
user := findUser(1)
if name, ok := user.Get(); ok {
fmt.Println("Found:", name) // Found: Alice
}
// Not found — with default
unknown := findUser(999)
fmt.Println("Name:", unknown.OrElse("Guest")) // Name: Guest
// Chaining
greeting := MapOptional(findUser(2), func(name string) string {
return "Hello, " + name + "!"
})
fmt.Println(greeting.OrElse("Hello, stranger!")) // Hello, Bob!
}
Стандартная библиотека: slices и maps (Go 1.21+)
Начиная с Go 1.21, стандартная библиотека включает пакеты slices и maps с обобщёнными функциями:
package main
import (
"cmp"
"fmt"
"maps"
"slices"
)
func main() {
// === slices package ===
nums := []int{3, 1, 4, 1, 5, 9, 2, 6}
// Sort
slices.Sort(nums)
fmt.Println("Sorted:", nums) // [1 1 2 3 4 5 6 9]
// Sort with custom comparison
slices.SortFunc(nums, func(a, b int) int {
return cmp.Compare(b, a) // reverse order
})
fmt.Println("Reverse:", nums) // [9 6 5 4 3 2 1 1]
// Contains
fmt.Println("Contains 5:", slices.Contains(nums, 5)) // true
// Index
fmt.Println("Index of 4:", slices.Index(nums, 4)) // 4
// Min, Max
fmt.Println("Min:", slices.Min(nums)) // 1
fmt.Println("Max:", slices.Max(nums)) // 9
// Compact (remove consecutive duplicates from sorted)
sorted := []int{1, 1, 2, 2, 3, 3, 3}
unique := slices.Compact(sorted)
fmt.Println("Compact:", unique) // [1 2 3]
// Clip (free unused capacity)
clipped := slices.Clip(unique)
fmt.Printf("Clip: len=%d cap=%d\n", len(clipped), cap(clipped))
// === maps package ===
m := map[string]int{
"go": 2009,
"rust": 2010,
"python": 1991,
}
// Keys and Values
keys := slices.Sorted(maps.Keys(m))
fmt.Println("Keys:", keys) // [go python rust]
vals := slices.Collect(maps.Values(m))
fmt.Println("Values:", vals)
// Clone
clone := maps.Clone(m)
clone["java"] = 1995
fmt.Println("Original:", m) // no "java"
fmt.Println("Clone:", clone) // has "java"
// Equal
fmt.Println("Equal:", maps.Equal(m, m)) // true
// DeleteFunc
maps.DeleteFunc(clone, func(k string, v int) bool {
return v < 2000 // delete languages from before 2000
})
fmt.Println("After delete:", clone) // [go:2009 rust:2010]
// Collect — create map from iterator (Go 1.23+)
// maps.Collect(iter)
}
Итераторы и slices (Go 1.23+)
package main
import (
"fmt"
"slices"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Chunk splits into sub-slices
for chunk := range slices.Chunk(nums, 3) {
fmt.Println(chunk)
}
// [1 2 3]
// [4 5 6]
// [7 8 9]
// [10]
// slices.All — iterator over index-value pairs (Go 1.23+)
for i, v := range slices.All(nums) {
if i >= 3 {
break
}
fmt.Printf(" [%d]=%d\n", i, v)
}
// slices.Backward — reverse iterator (Go 1.23+)
fmt.Println("Backward:")
for _, v := range slices.Backward(nums[:5]) {
fmt.Printf("%d ", v) // 5 4 3 2 1
}
fmt.Println()
}