Today we come to the core of the context
package: The Context
interface itself.
type Context
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key any) any }
A Context carries a deadline, a cancellation signal, and other values across API boundaries.
Context’s methods may be called by multiple goroutines simultaneously.
For clarity, I’ve removed all of the documentation for each of the interface methods in the above quote—we’ll get to those in following emails, and include those there.
For today, let’s just talk about the interface in general.
Because this value is an interface, anyone can create their own custom implementation. And you might be tempted to think that this means that any type you create that satisfies this interface is a valid context.Context
value.
But the API contract for context.Context
includes details not expressed in the type definition. In fact, most API contracts go beyond the type or function definition, so this serves as a good reminder of that fact.
In the case of Context
, for example, we might think that the Err()
method can return any error. I’ve even created custom Context
implementations that did this. But it turns out, this breaks the API contract:
If Done is not yet closed, Err returns nil. If Done is closed, Err returns a non-nil error explaining why: DeadlineExceeded if the context’s deadline passed, or Canceled if the context was canceled for some other reason.
In other words, Err()
may return three distinct values only: nil
, context.DeadlineExceeded
or context.Canceled
. Any other return value breaks this API contract.