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

Related Content

Zero values of slices and maps

Composite literals … Note that the zero value for a slice or map type is not the same as an initialized but empty value of the same type. Let’s look at examples so we can understand exactly what is being stated here. Recall that when you declare a variable of a given type, the default value is the zero value for the type: var s1 []string // Zero value for slice var m1 map[string]int // Zero value for map What the spec is telling us is that the zero value of a slice or map is not the same as an initialized, but empty, slice or map:

Taking the address of literals

Composite literals … Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal’s value. var pointer *Point3D = &Point3D{y: 1000} This particular feature of the language is immensely useful. Without it, the above code snippet would become much longer: var point Point3D = Point3D{y: 1000} var pointer *Point3D = &point In fact, this is the exact situation we are in for non-composites.

Operator constraints

Last week I inconspicuously skipped over one sentence in the spec, the last sentence in the section on Operands, and jumped straight ahead to the section on Qualified identifiers. This is because the sentence didn’t make immediate sense to me, and I needed time to research it. I’ve done that research now, so, let’s jump back and cover the missed territory. Operands … Implementation restriction: A compiler need not report an error if an operand’s type is a type parameter with an empty type set.