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.


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.

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

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.


Error interfaces

On Monday I talked about error structs. Let’s continue that theme, with error interfaces. To recap the problem: You want to the caller of your function to be able to extract context from an error, without parsing error strings. Or maybe you even want to include context that isn’t part of the error string at all! Monday’s solution to this problem had us using custom structs. A similar, but often more flexible approach, is to use an error interface.


Error structs

The standard library gives us only very basic error capabilities. For the most part, we can create strings-as-errors: return errors.New("oh no") And we can wrap errors with additional textual context: if err := doSomething(); err != nil { return fmt.Errorf("doSomething: %w", err) } But the fact that the built-in error type is an interface gives us a ton more flexibility than this to include arbitrary information with our errors. There are a few ways we can include additional information in errors, and I’ll go over a few of them.


100 episodes of Cup o' Go!

Yesterday, Shay Nehmad and I, and close to 20 other people, recorded the 100th episode of the Cup o’ Go podcast. It’s been just over two years since the first episode came out, and in that time we’ve made some good friends, and built a bit of a community. If you haven’t listened yet, I invite you to listen to the replay of the live episode, which is also about celebrating the newly released Go 1.