In Go 1.21, the standard library is significantly expanded

In Go 1.21, the standard library is significantly expanded

// теперь в Go так можно!
slices.Contains(s, v)

A year ago on Karuna’s blog, we wrote about generics in Go, and it was mentioned that the public community has split into two parts. Not everyone needed this innovation, especially in a simple product code. And it must be said, this is still the case, generics, as before, are not used by all projects.

However, for the Go standard library, it was a real godsend. New standard generalized functions have appeared and, having been supported in the experimental repository golang.org/x/exp, will now appear in Go 1.21. Release literally in a month.

TLDR: there are many functions for working with slices, maps, as well as a new logger with (almost) all the necessary chips.

Personally, a significant event for me was the appearance of the ability to search for an element in a slice and obtain map keys, because the time has come a long time ago, 10 years of language.

But let’s talk about everything in order.

1. Working with slices (“slices” package)

As they say, what the Bolsheviks talked about for so long happened. For the sake of clarity, I will show a few examples in the style of “has-been” so that those who do not know Go well can feel the situation.

Search for an element in a slice (any comparable-type element)

// было:
var contains bool
for i := range s {
    if v == s[i] {
        contains = true;
        break;
    }
}

// стало:
slices.Contains(s, v)

Sorting

Previously, it was possible to sort slices of only three types, for which there were separate functions of the sort package: Ints, Float64s, Strings.

For other types, for example, float32 or int64, it was necessary to hedge something like this:

// было
sort.Slice(s, func(i, j int) bool {
    return s[i] < s[j]
})

// стало (для любых ordered типов)
slices.Sort(s)

Search for the maximum

// было 
max := s[0]
for _, v := range s {
    if v > max {
        max = v
    }
}

// стало
maxVal := slices.Max(s)

Comparison of two slices

// было
func Equal(a, b []int) bool {
    if len(a) != len(b) {
        return false
    }
    for i, v := range a {
        if v != b[i] {
            return false
        }
    }
    return true
} 

// стало
areEqual := slices.Compare(a, b)

and many other functions, such as binary search, inserting inside a slice, etc. You can see the full list here.

2. Working with maps (“maps” package)

It’s interesting maps.Keys(m) and maps.Values(m), which return the keys and values ​​of the map, respectively. Those who have solved problems with litcode know how boring it is to write meaningless loops and distract from the main task.

ages := map[string]int{"John": 21, "Jack": 32}

// было:
var names []string
for name, _ := range ages {
    names = append(names, name)
}

// стало:
names := maps.Keys(ages)

Also, the maps package has functions for map comparison, cloning, copying, etc., see https://pkg.go.dev/maps

UPD.: for maps, changes are possible in the release (see comments)

3. New logger (“log/slog” package)

Thanks to the Cross Join tg channel for the news and information about the logger

I once studied various Go interview questionnaires where one of the standard questions was “tell me why no one uses the built-in logger”.

Well, nobody uses it, because it can’t do anything. As a result, third-party solutions zerolog, logrus, zap, etc. appeared in 99.9% of projects.

In the new version of Go, the log/slog package (play on words: slog is translated as “work”, “tiring”) has been added, which contains:

  • severity levels: Debug, Info, Warn, Error (or you can use any integer number)

  • option to use Handler: textHandler, jsonHandler or your own Handler that satisfies the interface

  • built-in ability to transfer key-value:

    // здесь message - это "hello",
    // и еще добавляется ключ-значение count=3
    logger.Info("hello", "count", 3)

    if used together with the json-handler, the output will be as follows:

    {"time":"2022-11-08T15:28:26.000000000-05:00","level":"INFO","msg":"hello","count":3}

  • these key-values ​​can be grouped, and get a nested Jason (slog.Group)

  • you can display a log with context

    InfoCtx(ctx, "message", "key", val)

  • all sorts of tricks for convenience and performance, for example, the With method, which returns a sub-logger, when creating which you can immediately set some attributes. The built-in handlers will only format them once.

What is missing:

There is no sampling, and this is important to many. I hope it will be finished in the future.
So far, it is not very clear what the speed is. They say zap is faster.

Conclusions

The standard library has significantly expanded, replenished with functions that should have been there a long time ago, but without generics it was much more difficult to implement them. As one of my acquaintances said, “if they also make error handling normal, I will definitely switch to Go.”

As for me, I will most likely try to throw out a third-party logger in one of the projects to reduce unnecessary dependencies. Well, there will be much less unnecessary cycles for slices and maps.

In general, the situation resembles the appearance of the grid in the CSS standard: people have been making html pages for 20+ years with God knows how: tables, floats, flexes, whatever, and finally someone figured out that people need to somehow arrange elements on the page. And grids appeared, where you can show “here are my columns.” Thank you for what happened. In my opinion, this should have been in the first version of CSS, and the rest of the whistles could have been postponed.

Same with Go: it’s clear that the standard library is deliberately small, but searching for an element in a slice is something that everyone needs from the beginning, probably in all languages.

That’s why I wrote in the title that the library has expanded “significantly”, because in the world of Go it’s business.

Related posts