Go

How does http.HandlerFunc work? Explain the function-type adapter pattern.

Question 106MediumGo 1.22 to 1.25

A function type can have methods, so a plain function can be adapted to satisfy a single-method interface without writing a struct:

// net/http
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }

// Usage: the conversion HandlerFunc(hello) is what makes hello a Handler
func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hi") }

var h http.Handler = http.HandlerFunc(hello)

// Middleware returning closures adapted to Handler
func Logging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    })
}

The same pattern appears in several places:

  • sort.Slice, which is the less-interface version.
  • gRPC's UnaryHandler.
  • The functional-options pattern (type Option func(*Config)).
  • Test doubles: type ReaderFunc func([]byte) (int, error) with a Read method lets you fake an io.Reader inline.

The interviewer wants to see that you understand the conversion HandlerFunc(hello) is necessary. hello by itself has no methods. The conversion gives it the method set of HandlerFunc. http.HandleFunc does that conversion for you.

More on Interfaces, Methods & Embedding

All 35 Interfaces, Methods & Embedding questions