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 {
	DoStuff()
}

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

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Get daily content like this in your inbox!

Subscribe