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.

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.


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.


Cancelation causes

We’ve seen that we can cancel contexts in a few ways–explicitly, or via timeouts. And that canceling a parent context also cancels all of its children. But in all of these cases we get only two possible error values: context.Canceled or context.DeadlineExceeded. What if you wish to express additional details about a cancelation? This is where the concept of a ‘cause’ comes into play… Overview … The WithCancelCause, WithDeadlineCause, and WithTimeoutCause functions return a CancelCauseFunc, which takes an error and records it as the cancellation cause.


Context cancelation

As already mentioned, the context.Context type serves two purposes: It manages cancelation signals, and it allows passing request-scoped data. In my opinion, these two purposes are distinct enough that they probably should be handled by two separate mechanisms. But alas, that’s not what we have. However, I think it’s still useful to think of the two uses as distinct. Most often (at least in a well-designed application), the primary purpose of a context is the propegation of cancelation signals.


Context plumbing

Monday we looked at a very high-level view of what problems the context package is meant to solve. Namely, to propagate cancelation signales and/or request-scoped values through the call stack of an application. Today we’ll take a brief look at how context “plumbing” works—and answer they why of all those mysterious context.Context function parameters you’ve probably seen, being passed around more than a virus in a daycare. Overview … Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context.


context

Hey! My apologies for being absent for the last few weeks. I’ve been incredibly busy closing on a new house and moving. We’re still settling, but it’s time to start this up again. So today I’m starting a short series on context! Specifically, I’ll be going through the context package in the standard library, explaining what it does, and offering my advice, much like I went through the Go spec in the past.