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’s pretty massive.
- New numbering scheme, so the
21
in1.21.0
is a Go language version. However still under v1 compatibility promise - There are many changes aroung
GOTOOLCHAIN
and the fact go1.21
ingo.mod
has now a meaning - The ability to download and use a specific toolchain version.
- Profile Guided Optimizations
- Improvements in type inference for generics
min
,max
andclear
builtins- shiny new
slices
,maps
,log/slog
andcmp
standard library packages - Experiment with a fixing loop variable capture
- Experimental WASI port (
GOOS=wasip1 GOARCH=wasm
) and ago:wasmimport
directive
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 response
I’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
stop
returnstrue
, the call preventsfunc()
from being run - if returns
false
then thefunc()
has been already started or stopped stop
does 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