Pointers to interfaces

March 27, 2023

As I mentioned last week, a pointer in Go may have any base type. This includes interfaces.

type MyInterface interface {

var x *MyInterface
var y *interface{}
var z ****interface{ Foo() }

Now while these are all valid, they are rarely useful. And this often trips people up.

As a rule: Never use a pointer to an interface.

There is only one exception of which I am aware. And it’s so unusual, that you can generally just adhere to the above rule. But here is the exception:

A pointer to an interface is useful when you need to replace an interface value in place. Actually, this use doesn’t only relate to interfaces, but it’s the only use case I know of that also extends to interfaces. Let me offer two examples:

  1. You want to modify the value of a function argument.

    This is by far the most common, but it’s also not immediately obvious that it uses pointers to interfaces. An ubiquitious example would be the json.Unmarshal function, which has the signature func Unmarshal(data []byte, v any) error.

    “But there’s no pointer to an interface there! That’s just a regular interface!”

    Ah, yes. But… the argument passed to json.Unmarshal must be a pointer.

    var result any
    if err := json.Unmarshal(input, &result) { // <---- See that pointer to `result`? That's a pointer to an interface.
  2. The “Holder pattern”

    This is really an extention of the above concept, but doesn’t apply only to function arguments. Here’s a simplified example of a pattern I’ve used in HTTP server logging middleware:

    func loggingMiddleware(next http.Handler) http.Handler {
    	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    		var metadata any
    		ctx := context.WithValue(r.Context(), "metadata", &metadata)
    		next.ServeHTTP(w, r.WithContext(ctx))
    		log.Printf("Request was handled (metadata=%v)", *metadata)
    // Then later in your call chain...
    	metadata := r.Context().Value("metadata").(*any)
    	*metadata = map[string]string{"metadata": "interesting"}

    This allows the code later in the call chain to circumvent the immutability of context values, even when using an interface… evil, right? Yeah. You should rarely, if ever, actually do that.

If that’s confusing to you, don’t worry about it. Like I said above: Never use a pointer to an interface 😊

Share this

Related Content

Type identities of other types

Type identity … Two pointer types are identical if they have identical base types. Given: type ( P = *int Q = P R *int ) P, Q, and *int are all identical to each other, but different from *int32 and R. Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is.

Implementing an interface

It feels like it’s been a month since we started on interfaces. Today we’re covering the final section on this topic! Implementing an interface A type T implements an interface I if T is not an interface and is an element of the type set of I; or T is an interface and the type set of T is a subset of the type set of I. A value of type T implements an interface if T implements the interface.

Recursive interfaces

Check out this week’s episode of the Cup o’ Go podcast: What the ʕ◔ϖ◔ʔ? New merch, TDD book interview with Adelina Simion, and more You can probably guess Go’s rules about recursively embedding interfaces. In short: It’s not allowed. General interfaces … An interface type T may not embed a type element that is, contains, or embeds T, directly or indirectly. // illegal: Bad may not embed itself type Bad interface { Bad } // illegal: Bad1 may not embed itself using Bad2 type Bad1 interface { Bad2 } type Bad2 interface { Bad1 } // illegal: Bad3 may not embed a union containing Bad3 type Bad3 interface { ~int | ~string | Bad3 } // illegal: Bad4 may not embed an array containing Bad4 as element type type Bad4 interface { [10]Bad4 } Note however, that, an interface method may refer to the interface itself.