Context package variables

The context package exports two variables: Variables var Canceled = errors.New("context canceled") Canceled is the error returned by [Context.Err] when the context is canceled for some reason other than its deadline passing. var DeadlineExceeded error = deadlineExceededError{} DeadlineExceeded is the error returned by [Context.Err] when the context is canceled due to its deadline passing. That’s it. These are the only two types of errors that a context’s Err() method is allowed to return.


Concurrent use of contexts

Overview … The same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines. Contexts are built using layers. Every time you call one of the context.With* functions, the orginal context is wrapped in a new layer. This is the “magic” that makes contexts concurrency safe by design–they’re never mutated once they’re created. ctx := context.Background() // Create an original context ctx, cancel = context.


When not to use context values

Storing values in a context is quite flexible, but comes without strict type safety, and obscures the API. So what can we do about this? First, for type safety, we’re limited to runtime assertions: userID := ctx.Value(userIDKey).(string) But this can panic if we ever get an unexpected value (or even nil). So to make it safer, we can use the two-variable assignment form, which yields a bool indicating success: userID, ok := ctx.


Context values and type safety

Last week I asked whether or not it’s a good idea to pass things like a user-scoped database handle or logger via a context value. Before I provide my direct answer, I want to take a short detour… Go is a (mostly) strictly-typed language. This gives us certain guarantees. When we have a variable of type int, we know it doesn’t contain the value "cow", because that’s not an integer.

Subscribe to Boldly Go: Daily

Every day I'll send you advice to improve your understanding of Go. Don't miss out! I will respect your inbox, and honor my privacy policy.

Unsure? Browse the archive.


Context abuse

We’re nearly through the overview of the context package, when we come across this seemlingly straightforward sentence: Overview … Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions. But is that really so straight forward? Let’s consider some examples. Let’s first tackle what probably is straight-forward: For garden variety optional parameters, context is the wrong tool! Do this: // Connect connects to the service using the provided host and port, or the // default host and/or port if omitted.


T.Context

After my last email, discussing context.TODO(), I got this question from Bruno Schaatsbergen: Thanks for the blog Jonathan, as always a pleasure to read. Regarding the last part on testing, any particular reason why you do not recommend using the new T.Context and B.Context methods? Great question! Go 1.24 added new methods to the testing package: T.Context, B.Context, and F.Context. These methods return a context which is canceled when the test, benchmark, or fuzz test, respectively, is canceled.


context.TODO

Overview … Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use. Two pieces of advice in today’s section. I’m going to tackle them in reverse, though. Use context.TODO() if you are unsure which Context to use. Functionally context.TODO() is identical to context.Background(). In fact, the only difference between the two, is their implementation of the String() method, as we can see by reading the source:


Context arguments

Overview … The Context should be the first parameter, typically named ctx: func DoSomething(ctx context.Context, arg Arg) error { // ... use ctx ... } This short sentence includes two “rules”. Let’s talk about both, why they exist, and when to (possibly) violate them. Context should be the first parameter. Why this is a rule is probably a lot less important than following the rule. But let’s consider the why anyway.


What's wrong with this code? ANSWERED!

On Friday, I shared a short code snippet, and asked how it could be improved. If you missed it, go read the challenge now then come back. I got a good number of responses, so today I’m going to share a few of them, and offer my own answer. First off, Jim Kalafut, and several others, the code I shared. One line was return nil, but should have been return decodedBytes, nil.


What's wrong with this code?

Today I want to share a bit of code I found in the wild, and challenge you to improve it. First off, let me make it clear that this code “works”. It has unit tests, and the tests all pass correctly. So I’m not looking for bugs, per se. func decodeString(encoded string) ([]byte, error) { var decodedBytes []byte var err error trimmed := strings.TrimRight(encoded, "=") decodedBytes, err = base64.StdEncoding.DecodeString(trimmed) if err !


Context rules

Overview … Programs that use Contexts should follow these rules to keep interfaces consistent across packages and enable static analysis tools to check context propagation: Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. This is discussed further in https://go.dev/blog/context-and-structs. We’ve already discussed how the proliferation of the ctx context.Context argument can start to look more cluttered than my desk on a Friday afternoon.