Software Development

Examining Go 1.21 – the Latest Golang Release

Home

>

Blog

>

Software Development

>

Examining Go 1.21 – the Latest Golang Release

Published: 2023/12/21

5 min read

At the beginning of the August, the newest Golang version was introduced by the Go team. Go 1.21 is with us, and after its initial few months, let me share with you the most interesting features it brings.

The versioning

The first change in the latest Go version is related to the versioning itself. In the past, a version like “Go 1.20” represented both the language version and its initial implementation. However, with the latest release, a distinction has been introduced. While Go version 1.21 applies to all modifications, the latest implementation of the language installed on your computer will be referred to as Go 1.21.0. Also, the `go.mod` file will have the full version with all parts: major, minor and patch – which is different than before. This seemingly minor change is an interesting curiosity in terms of the language versioning.

Build-ins

In the latest release of Go, developers can take advantage of three new built-in functions: `min`, `max` and `clear`. These three functions come with quite self-explanatory, yet very useful features. Importantly, each is a generic one – this fact addresses the concerns I expressed in my previous article, Generics: What Go Developers Need to Know. It’s another sign of the living and continuously developing environment of the Go language.

Here are few examples of the mentioned build-ins:


func TestMin(t *testing.T) { 

     //basic 

     x := min(1, 2)
     fmt.Println("TestMin1", x) 

     //types combination 
     y := min(-10, 2.0, 0.5) 
     fmt.Printf("TestMin2: %v(%T)\n", y, y) 

     // invalid 
     // min(1, "2") 

     // lexical comparison 
     z1 := min("function", "min") 
     fmt.Println("TestMin3:", z1) 
} 

func TestMax(t *testing.T) { 

     // same rules 
     x := max(1, 2) 
     fmt.Println("TestMax1:", x) 
} 

 

type Val struct { 
} 

func TestClear(t *testing.T) { 
     var x map[string]int 
     // assignment to nil will panic 
     // x["a"] = 1 

     // clear of nil is valid 
     clear(x) 

     x = map[string]int{"a": 1, "b": 0, "c": 2} 
     fmt.Println(x) 
     clear(x) 
     fmt.Println(x) 

     y := map[string]Val{"a": Val{}, "b": Val{}, "c": Val{}} 
     fmt.Println(y) 
     clear(y) 
     fmt.Println(y) 
} 

Feel free to copy this code and play with it. You can simply paste in into some “_test.go” file and run the tests or use prepared example on Go’s Playground.

Regarding the displayed `min` and `max` functions – these two are the generic ones but it doesn’t mean they accept every type. The scope is indeed wide but needs to fulfil following interface from the ` cmp` package:


type Ordered interface { 
      ~int | ~int8 | ~int16 | ~int32 | ~int64 | 
      ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | 
      ~float32 | ~float64 | 
      ~string
} 

Type inference

Another important development worth noting is that the `type inference` system has some enhancements. These may be hard to notice at first sight but in general, the newest Go improves the precision and predictability of this feature. Also, in the language specification, there have been corrections and improvements in the description of the `type inference` system, ensuring better clarity and understanding for developers.

Backward compatibility

Go 1.21 introduces improvements in backward compatibility by the use of the `GODEBUG` environment variable. This enables developers to have control over default behaviour changes that could potentially break existing programs. You might ask yourself “Aren’t Golang versions backward compatible?”. The answer is yes, they are – but it’s about bug fixes. If the behavior of the code relied on the buggy code, fixing that code does not break the backward compatibility promise but still could break existing code. The aforementioned environment variable usage ensures that programs relying on specific versions of Go can continue to function correctly without being affected by newer changes. Details of this feature are out of the scope of this article but if you’re interested, check out this feature documentation.

Libraries – new and improved

Go version 1.21 brings several changes to various packages. Here are some most interesting ones:

– the new `log/slog` package introduces structured logging with levels, enabling fast and precise processing of log data

– the `slices` package provides common operations on slices (with generics support) such as: finding min/max, sorting, deleting or shallow copying

– the `maps` package, though smaller in comparison to the one for slices, also provides some common operations for this type of data

– The `cmp` package defines the `Ordered` type constraint and introduces two generic functions: `Less` and `Compare`, which are useful for ordered types.

– The `context` package introduces new functions to control context cancellations (through the `WithoutCancel` function that allows users to cut child context from a parent) and modifications (like `AfterFunc` that enables users to register a handler that will be run after context cancellation)

– The `errors` package adds the `ErrUnsupported` error for unsupported operations

Example of `slices` package usage:


type myStruct struct { 
      name   string 
      values []string 
} 


func TestSlices(t *testing.T) { 

      { 

                  /* 

                             re-assigning 

                  */ 

                  x := []string{"original"} 
                  new := x 
                  fmt.Println("old1", x) 
                  fmt.Println("new1", new) 


                  new[0] = "modified" 
                  fmt.Println("old2", x) 
                  fmt.Println("new2", new) 
      } 
      { 

                  /* 

                             shallow copy of slice of strings 

                  */ 

                  x := []string{"original"} 


                  new := slices.Clone(x) 
                  fmt.Println("old1", x) 
                  fmt.Println("new1", new) 


                  new[0] = "modified" 
                  fmt.Println("old2", x) 
                  fmt.Println("new2", new) 

      } 
      { 

                  /* 

                              shallow copy of objects 

                  */ 

                  ms := myStruct{ 
                  name:   "first", 
                  values: []string{"original"}, 

                  } 

                  x := []myStruct{ms}   

                  new := slices.Clone(x) 
                  fmt.Println("old1", x) 
                  fmt.Println("new1", new) 

                  new[0].name = "second"
                  new[0].values[0] = "modified" 
                  fmt.Println("old2", x) 
                  fmt.Println("new2", new) 
                  // after the copy, references to objects remain the same (`values` field) 
                  // but values are independent (`name` field) 

         } 

} 

Feel free to copy this code and play with it. You can simply paste in into some “_test.go” file and run the tests or use prepared example on Go’s Playground.

Stable growth

Not every Go version introduces groundbreaking changes. Some are just smaller batches of improvements and fixes for the language environment. And Go 1.21 is one such version. It doesn’t mean it’s pointless – for sure it provides some nice features and language improvements which, as I’ve described, both the language and community needed. Therefore, in my opinion the latest version of Go is at it should be. After all, mature technologies like stable growth. Not every update will turn the world upside down.

Fill out the contact form to learn how Software Mind’s cross-functional development teams can support your engineering projects.

About the authorMarcin Plata

Senior Software Engineer

A backend developer with 4 years of experience, Marcin first started programming in Python, but eventually fell in love with Go. In his daily work as a senior software engineer, Marcin creates and maintains microservices that work in systems based on event-driven architecture. An avid tech enthusiast and Go disciple, Marcin enjoys keeping up to date with the latest programming developments and sharing new knowledge with colleagues and clients.

Subscribe to our newsletter

Sign up for our newsletter

Most popular posts