In a previous post we introduced iterators, a new feature in the Go language added in Go 1.23. In this blog post, we will look into the enhancements made to the slices and maps packages. These changes are built on top of iterators, so would not be possible before this release.

Exploring the New Features in Go 1.23: Iterators and Enhancements to the Slices & Maps Packages


In a previous post we introduced iterators, a new feature in the Go language added in Go 1.23. In this blog post, we will look into the enhancements made to the slices and maps packages. These changes are built on top of iterators, so would not be possible before this release.

If you’d like to learn more about iterators, you can read our post on it here.

Slice Package

The slices package adds several functions that work with iterators:

All

All returns an iterator over slice indexes and values.

package main

import (
    "fmt"
    "slices"
)

func main() {
    s := []string{"bytesize", "go", "dot", "com"}
    for i, v := range slices.All(s) {
        fmt.Printf("%d:%v ", i, v)
    }
}

Values

Values returns an iterator over slice elements.

package main

import (
    "fmt"
    "slices"
)

func main() {
    s := []string{"bytesize", "go", "dot", "com"}
    for v := range slices.Values(s) {
        fmt.Printf("%v ", v)
    }
}

Backward

Backward returns an iterator that loops over a slice backward.

package main

import (
    "fmt"
    "slices"
)

func main() {
    s := []string{"bytesize", "go", "dot", "com"}
    for i, v := range slices.Backward(s) {
        fmt.Printf("%d:%v ", i, v)
    }
}

Collect

Collect collects values from an iterator into a new slice.

package main

import (
    "fmt"
    "slices"
)

func main() {
    s := []string{"bytesize", "go", "dot", "com"}
    s1 := slices.Collect(slices.Values(s))
    fmt.Println(s1)
}

AppendSeq

AppendSeq appends values from an iterator to an existing slice.

package main

import (
    "fmt"
    "slices"
)

func main() {
    s := []string{"bytesize", "go", "dot", "com"}
    t := []string{"the", "best", "go", "content"}
    u := slices.AppendSeq(s, slices.Values(t))
    fmt.Println(u)
}

Sorted

Sorted collects values from an iterator into a new slice, and then sorts the slice.

package main

import (
    "fmt"
    "slices"
)

func main() {
    s := []string{"bytesize", "go", "dot", "com"}
    u := slices.Sorted(slices.Values(s))
    fmt.Println(u)
}

SortedFunc

SortedFunc is like Sorted but with a comparison function.

package main

import (
    "cmp"
    "fmt"
    "slices"
)

func main() {
    type course struct {
        name   string
        rating int
    }
    s1 := []course{{"debug", 98}, {"cli", 99}, {"grpc", 100}}
    compare := func(c1, c2 course) int {
        return cmp.Compare(c1.rating, c2.rating)
    }
    s2 := slices.SortedFunc(slices.Values(s1), compare)
    fmt.Println(s2)
}

SortedStableFunc

SortedStableFunc is like SortFunc but uses a stable sort algorithm.

package main

import (
    "cmp"
    "fmt"
    "slices"
)

func main() {
    type course struct {
        name   string
        rating int
    }
    s1 := []course{{"debug", 98}, {"cli", 99}, {"grpc", 100}}
    compare := func(c1, c2 course) int {
        return cmp.Compare(c1.rating, c2.rating)
    }
    s2 := slices.SortedStableFunc(slices.Values(s1), compare)
    fmt.Println(s2)
}

Chunk

Chunk returns an iterator over consecutive sub-slices of up to n elements of a slice.

package main

import (
    "fmt"
    "slices"
)

func main() {
    type course struct {
        name   string
        rating int
    }
    s1 := []course{{"debug", 98}, {"cli", 99}, {"grpc", 100}}
    chunked := slices.Chunk(s1, 1)
    for v := range chunked {
        fmt.Printf("%v ", v)
    }
}

Maps Package

The maps package adds several functions that work with iterators too!

All

All returns an iterator over key-value pairs from a map.

package main

import (
    "fmt"
    "maps"
)

func main() {
    m := map[string]int{"bytesize": 1, "go": 2, "dot": 3, "com": 4}
    for k, v := range maps.All(m) {
        fmt.Printf("%v:%v ", k, v)
    }
}

Keys

Keys returns an iterator over keys in a map.

package main

import (
    "fmt"
    "maps"
)

func main() {
    m := map[string]int{"bytesize": 1, "go": 2, "dot": 3, "com": 4}
    for k := range maps.Keys(m) {
        fmt.Printf("%v ", k)
    }
}

Values

Values returns an iterator over values in a map.

package main

import (
    "fmt"
    "maps"
)

func main() {
    m := map[string]int{"bytesize": 1, "go": 2, "dot": 3, "com": 4}
    for v := range maps.Values(m) {
        fmt.Printf("%v ", v)
    }
}

Insert

Insert adds the key-value pairs from an iterator to an existing map.

package main

import (
    "fmt"
    "maps"
)

func main() {
    m := map[string]int{"bytesize": 1, "go": 2}
    newItems := map[string]int{"dot": 3, "com": 4}
    maps.Insert(m, maps.All(newItems))
    fmt.Println(m)
}

Collect

Collect collects key-value pairs from an iterator into a new map and returns it.

package main

import (
    "fmt"
    "maps"
)

func main() {
    newItems := map[string]int{"dot": 3, "com": 4}
    m := maps.Collect(maps.All(newItems))
    fmt.Println(m)
}

Conclusion

Go 1.23’s enhancements to the slices and maps packages, alongside the new iter package, bring a new level of flexibility to data handling in Go. While iterators add a layer of complexity, they also provide a way to write cleaner, more efficient code when dealing with collections.

These updates allow Go developers to handle collections in ways previously unavailable in the language, especially when it comes to generating sequences of values and working with data iteratively. As these features get more widely used, we’ll see if they become a staple in Go development or if they are only used in niche scenarios.

To learn more about all the new features in Go 1.23, check out our course Go 1.23 in 23 minutes.