Context in Structs

Contexts should not be stored inside a struct type, but instead passed to each method as a first parameter

Storing context in a struct prevents callers from passing individual contexts to methods. This results in a server whose requests lack individual contexts, making it impossible to properly handle cancellation. It also prevents propagation of trace IDs or other values from the caller. Additionally, the API becomes more confusing since it is unclear which methods the stored context applies to

There are exceptions to this rule. For example, satisfying the interface io.Reader or io.Writer is a good reason to put a context in a struct.  But to avoid issues discussed above, binding to context should be done locally to the use:

func (s *S) IO(ctx context.Context) io.ReadWriter {
	// return a struct that implements Read and Write on s using ctx
}

Then code can use s.IO(ctx) as an io.Reader or io.Writer:

func Foo(ctx context.Context, s *S) {
	fmt.Fprintln(s.IO(ctx), "hello, world")
}

Avoiding Allocations for Context Keys

The following values can be used as context key values to avoid allocation when passing a value as any in a context.WithValue call:

References