Go programming language has a new version 1.21.
Release Notes. Each new release contains a lot of great stuff and 1.21 is not an exception. In fact it is pretty massive.
- New numbering scheme, so the
21in1.21.0is a Go language version. However still under v1 compatibility promise - There are many changes aroung
GOTOOLCHAINand the fact go1.21ingo.modhas now a meaning - The ability to download and use a specific toolchain version.
- Profile Guided Optimizations
- Improvements in type inference for generics
min,maxandclearbuiltins- shiny new
slices,maps,log/slogandcmpstandard library packages - Experiment with a fixing loop variable capture
- Experimental WASI port (
GOOS=wasip1 GOARCH=wasm) and ago:wasmimportdirective
It takes a time to appreciate all of those. However the improvements of a
context package may be the easiest to grok. And to blog about of course.
WithoutCancel
WithoutCancel returns a copy of a context object, which is not canceled when parent is. But the context retains all the values of its parent. This is handy when passing a data to the further asynchronous processing and want to retain all the stored context values.
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx1, cancel1 := context.WithCancel(context.TODO())
ctx1 = context.WithValue(ctx1, "key", "value")
ctx2 := context.WithoutCancel(ctx1)
time.AfterFunc(250*time.Millisecond, cancel1)
<-ctx1.Done()
fmt.Printf("ctx1.Err()=%+v, ctx1.Value(key)=%#v\n",
ctx1.Err(), ctx1.Value("key"))
fmt.Printf("ctx2.Err()=%+v, ctx2.Value(key)=%#v\n",
ctx2.Err(), ctx2.Value("key"))
}ctx1.Err()=context canceled, ctx1.Value(key)="value"
ctx2.Err()=<nil>, ctx2.Value(key)="value"Cause
One of the most frustrating experiences with Go and context objects is the
ubiquitous request failed: context cancelled errors spread everywhere. Very
rarely they provide any clue about what’s happening.
The new Cause mechanism is about to solve
that. The WithCancel/WithDeadline and WithTimeout functions has
counterparts allowing to add additional information why the cancel was called.
package main
import (
"context"
"fmt"
)
func main() {
ctx, cancel := context.WithCancelCause(context.TODO())
cancel(fmt.Errorf("got bored waiting on a response"))
fmt.Printf("ctx.Err()=%+v\n", ctx.Err())
fmt.Printf("ctx.Cause()=%+v\n", context.Cause(ctx))
}ctx.Err()=context canceled
ctx.Cause()=got bored waiting on a responseI’d prefer ctx.Err() to return got bored waiting on a response: context canceled. However I assume this would break soo much code doing if ctx.Err() == context.Canceled, so adding an extra helper is probably safer option.
AfterFunc
AfterFunc is probably the most complicated addition to the stdlib.
- it runs a function
func()after context is Done in own goroutine - in case it’s already done, it fires the goroutine immediately
- returns
stop func() bool - if
stopreturnstrue, the call preventsfunc()from being run - if returns
falsethen thefunc()has been already started or stopped stopdoes not wait onfunc()to finish- multiple calls operated independently, so there can be a slice of functions attached to context cancellation
More advanced examples are in documentation. Here is a short example illustrating most of the properties mentioned earlier.
package main
import (
"context"
"log"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.TODO())
// stop1 called immediatelly
// will return true, this log won't be printed
stop1 := context.AfterFunc(ctx, func() {
log.Print("AfterFunc1: stopped") })
log.Printf("stop1 = %t", stop1())
// this will be printed as no stop is going to be called
_ = context.AfterFunc(ctx, func() {
log.Print("AfterFunc2: not stopped") })
time.AfterFunc(50*time.Millisecond, cancel)
<-ctx.Done()
// this will be printed as context is done
stop3 := context.AfterFunc(ctx, func() {
log.Print("AfterFunc3: after Done") })
// will print false as context is done
log.Printf("stop3 = %t", stop3())
// to give goroutines enough time to print
time.Sleep(250 * time.Millisecond)
}2009/11/10 23:00:00 stop1 = true
2009/11/10 23:00:00 AfterFunc2: not stopped
2009/11/10 23:00:00 stop3 = false
2009/11/10 23:00:00 AfterFunc3: after Done