# Modern Go Collection Patterns for Slices and Maps: A Complete Go 1.21+ Guide

> Discover modern Go collection patterns for slices and maps in Go 1.21+. Learn how to use new built-in functions to simplify your code and improve performance.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: deep-dive
- Published: 2026-08-29

---

**Go 1.21 introduced standardized collection utilities in the `slices` and `maps` packages that replace manual loops with declarative functions like `slices.Contains`, `maps.Clone`, and `slices.Compact`.**

The [JetBrains/go-modern-guidelines](https://github.com/JetBrains/go-modern-guidelines) repository catalogs these idiomatic patterns to help developers write cleaner, more maintainable Go code. According to the project's [FEATURES.md](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md), these utilities eliminate boilerplate for common operations on slices and maps, reducing the need for error-prone manual iteration.

## Slices Package Patterns

The `slices` package, introduced in Go 1.21, provides generic functions that operate on slices of any ordered or comparable type.

### slices.Contains: Replace Manual Search Loops

Instead of iterating with a `for` loop to check membership, use `slices.Contains` for O(n) existence checks.

```go
// Before: Manual iteration
found := false
for _, v := range s {
    if v == needle {
        found = true
        break
    }
}

// After: Declarative check
found := slices.Contains(s, needle)

```

As documented in the [JetBrains/go-modern-guidelines](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md#slicescontains), this pattern eliminates the need for explicit loop control variables and break statements.

### slices.Sort and slices.SortFunc: Simplified Sorting

The legacy `sort.Slice` requires reflection and interface conversions. The modern `slices.SortFunc` uses generics for type-safe comparisons with `cmp.Compare`.

```go
// Before: sort.Slice with reflection overhead
sort.Slice(users, func(i, j int) bool {
    return users[i].Name < users[j].Name
})

// After: Type-safe generic sorting
slices.SortFunc(users, func(a, b User) int {
    return cmp.Compare(a.Name, b.Name)
})

```

For primitive types that satisfy `constraints.Ordered`, use `slices.Sort` directly without a comparator function.

### slices.Index: Find Element Positions

Replace manual index tracking with `slices.Index` to return the first index of a matching element, or -1 if absent.

```go
// Before: Counter-based search
idx := -1
for i, v := range s {
    if v == needle {
        idx = i
        break
    }
}

// After: Direct index lookup
idx := slices.Index(s, needle)

```

This pattern is cataloged under the [range-over-index](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md#rangeoverindex) guideline in the repository.

### slices.Reverse: In-Place Reversal

Eliminate explicit swap loops when reversing slice order.

```go
// Before: Manual swap loop
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
    s[i], s[j] = s[j], s[i]
}

// After: Single function call
slices.Reverse(s)

```

### slices.Compact: Remove Consecutive Duplicates

Use `slices.Compact` to filter adjacent duplicate values without maintaining a separate seen-map.

```go
// Before: Conditional append logic
result := s[:1]
for i := 1; i < len(s); i++ {
    if s[i] != s[i-1] {
        result = append(result, s[i])
    }
}

// After: Compact consecutive duplicates
result := slices.Compact(s)

```

Note that `slices.Compact` modifies the slice in place and returns a truncated slice; it does not eliminate non-consecutive duplicates.

## Maps Package Patterns

The `maps` package provides generic utilities for copying and manipulating map collections.

### maps.Clone: Efficient Map Copying

Replace manual allocation and iteration when creating independent copies of maps.

```go
// Before: Manual copy loop
copy := make(map[string]int, len(m))
for k, v := range m {
    copy[k] = v
}

// After: One-line clone operation
copy := maps.Clone(m)

```

According to [FEATURES.md](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md#mapsclone), `maps.Clone` handles nil maps safely and optimizes allocation based on the source map's size.

### maps.DeleteFunc: Conditional Deletion

Remove entries matching a predicate without manual range loops that require map modification safety checks.

```go
// Before: Range with delete (requires care with map modification)
for k, v := range m {
    if shouldDelete(k, v) {
        delete(m, k)
    }
}

// After: Declarative conditional deletion
maps.DeleteFunc(m, shouldDelete)

```

### maps.Keys and maps.Values: Extract Collections

While `maps.Keys` and `maps.Values` return iterators, combine them with `slices.Collect` (Go 1.23+) to materialize slices.

```go
// Before: Manual slice construction
keys := make([]string, 0, len(m))
for k := range m {
    keys = append(keys, k)
}

// After: Iterator to slice conversion (Go 1.23+)
keys := slices.Collect(maps.Keys(m))
vals := slices.Collect(maps.Values(m))

```

Note that `slices.Collect` requires Go 1.23 or later, though `maps.Keys` and `maps.Values` are available in Go 1.21+ as iterators.

## The clear Builtin: Reset Collections

Go 1.21 introduced the `clear` builtin, which efficiently empties maps or zeroes slice elements.

```go
// Before: Map deletion loop
for k := range m {
    delete(m, k)
}

// After: Clear all entries
clear(m)

// Before: Slice zeroing
for i := range s {
    s[i] = zero
}

// After: Zero all elements
clear(s)

```

This applies to both maps and slices, replacing explicit deletion or zeroing loops as documented in the [clear collection](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md#clearcollection) pattern.

## Summary

- **Use `slices.Contains`** instead of manual `for` loops to check element existence in slices.
- **Prefer `slices.SortFunc`** over `sort.Slice` for type-safe, generic sorting without reflection overhead.
- **Adopt `maps.Clone`** to duplicate maps in a single line without manual iteration.
- **Leverage `maps.DeleteFunc`** for predicate-based entry removal without loop complexity.
- **Apply `clear`** to empty maps or zero-out slices efficiently in O(n) time.
- **Combine `maps.Keys`/`Values` with `slices.Collect`** (Go 1.23+) to extract keys or values into slices.
- **Use `slices.Compact`** and **`slices.Reverse`** for in-place slice transformations that previously required manual index manipulation.

## Frequently Asked Questions

### What Go version introduced the slices and maps packages?

The `slices` and `maps` packages were introduced in **Go 1.21** as part of the standard library's generics expansion. While `slices.Collect` requires Go 1.23, the core collection utilities are available from Go 1.21 onward.

### How do slices.Sort and the legacy sort.Slice differ?

`slices.Sort` and `slices.SortFunc` use generics to accept the slice directly and a comparison function with concrete types, eliminating the reflection and interface{} conversions required by `sort.Slice`. This results in better performance and compile-time type safety.

### Is slices.Collect available in Go 1.21?

No, `slices.Collect` was introduced in **Go 1.23** to convert iterators into slices. In Go 1.21 and 1.22, you must manually append from `maps.Keys` or `maps.Values` iterators to build slices.

### How does maps.Clone handle nil maps?

`maps.Clone` returns `nil` when passed a `nil` map, maintaining the same behavior as manually checking for nil before allocation. This prevents panic conditions and matches the zero-value semantics of the original map.