Understanding small piece of code in standard Library (Context package)

Hi folks,

Go newbie here. I am trying to wrap my head around this piece of code found in the context package.

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	c := newCancelCtx(parent)
	propagateCancel(parent, &c)
	return &c, func() { c.cancel(true, Canceled) }
}

// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
	return cancelCtx{Context: parent}
}

type cancelCtx struct {
	Context

	mu       sync.Mutex            // protects following fields
	done     atomic.Value          // of chan struct{}, created lazily, closed by first cancel call
	children map[canceler]struct{} // set to nil by the first cancel call
	err      error                 // set to non-nil by the first cancel call
}

What I do not understand is the return statement in the WithCancel function :
return &c, func() { c.cancel(true, Canceled) }

How can the function return a pointer to a CancelCtx (the &c) when it is expecting a Context? I understand that Context is an embedded field of CancelCtx but I still don’t get how this works (and why is a pointer being returned?).

I believe I am missing some concept or feature of the language here and would appreciate some guidance.

Hi @GoKim.

I agree this looks kind of wrong.

But consider that the type of Context is interface.

&c implements that interface, because its type cancelCtx implements all its methods with a pointer receiver:

type cancelCtx struct {
	// ...
}

// all methods of cancelCtx have pointer receivers:

func (c *cancelCtx) Value(key any) any {
	// ...
}

func (c *cancelCtx) Done() <-chan struct{} {
	// ...
}

func (c *cancelCtx) Err() error {
	// ...
}

This is why WithCancel can legitimately return a pointer to the struct.

1 Like

Thanks a lot @christophberger that made it crystal clear.

1 Like