Search code examples
structinterfacegocomposition

Don't understand composition in Go


In the example below I've embedded http.ResponseWriter into my own struct called Response. I've also added an extra field called Status. Why can't I access that field from inside my root handler function?

When I print out the type of w in my root handler function it says it's of type main.Response which seems correct and when I print out the values of the struct I can see that Status is there. Why can't I access by going w.Status?

This is the contents of stdout:

main.Response
{ResponseWriter:0xc2080440a0 Status:0}

Code:

package main

import (
    "fmt"
    "reflect"

    "net/http"
)

type Response struct {
    http.ResponseWriter
    Status int
}

func (r Response) WriteHeader(n int) {
    r.Status = n
    r.ResponseWriter.WriteHeader(n)
}

func middleware(h http.Handler) http.Handler {

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        resp := Response{ResponseWriter: w}

        h.ServeHTTP(resp, r)
    })
}

func root(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("root"))
    fmt.Println(reflect.TypeOf(w))
    fmt.Printf("%+v\n", w)
    fmt.Println(w.Status) // <--- This causes an error.
}

func main() {
    http.Handle("/", middleware(http.HandlerFunc(root)))
    http.ListenAndServe(":8000", nil)
}

Solution

  • w is a variable of type http.ResponseWriter. ResponseWriter does not have a field or method Status, only your Response type.

    http.ResponseWriter is an interface type, and since your Response type implements it (because it embeds ResponseWriter), the w variable may hold a value of dynamic type Response (and in your case it does).

    But to access the Response.Status field, you have to convert it to a value of type Response. For that use Type assertion:

    if resp, ok := w.(Response); ok {
        // resp is of type Response, you can access its Status field
        fmt.Println(resp.Status) // <--- properly prints status
    }