One-function Interfaces in GoLang

  tr_cn        2025-03-30 10:55:16       2,496        0         

Saw a GoLang pattern where a function itself implements an interface—meaning the function alone is the implementation of the interface. It took me a while to figure this out when I first encountered it, so I’m recording it here.

Take server.go from the GoLang standard library as an example. The Handler interface is defined as follows:

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

If we want to define a Handler, we would typically write:

// Define a struct
type MyHandler struct{}

// Implement the http.Handler interface method
func (h MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello from MyHandler!")
}

http.Handle("/hello", MyHandler{})

There are two issues here:

  1. It’s a bit verbose.
  2. The ServeHTTP function, which is closest to the function content, is a required interface method. Its name is fixed but not meaningful, making all handler functions use the same method name.

In modern GoLang, we don’t write handlers this way anymore. Instead, we do:

func myHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
}

http.Handle("/hello", http.HandlerFunc(myHandler))

Why does this work? Because the source code contains these lines:

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// [Handler] that calls f.
type HandlerFunc func(ResponseWriter, *Request)

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

This short comment carries a deep meaning.

  1. The first line defines type HandlerFunc func(ResponseWriter, *Request), meaning our myHandler function now has the HandlerFunc type.
  2. Every HandlerFunc object has a method called ServeHTTP, which means it satisfies the Handler interface.
  3. The method implementation simply calls the function itself, since the object itself is a function.

In short, any function with the signature ServeHTTP(w ResponseWriter, r *Request) can be converted to a HandlerFunc object. Although it is a function, functions in GoLang are also objects. This means any function with this signature can serve as a Handler.

Thus, we can write:

func myHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
}
http.Handle("/hello", http.HandlerFunc(myHandler))

Why not define Handler directly as a function?

type Handler func(ResponseWriter, *Request)

Wouldn’t this achieve the same effect?

The reason is that Handler can be much more complex. For example, GoLang’s middleware mechanism is fundamentally based on chaining Handler calls. More complex Handler implementations may need to maintain internal state, where structs are more suitable than functions. For instance, the httpauth library first initializes a Handler before using it.

What if we still define Handler as a function? Third-party libraries could require initializing an object from their API first and then provide functions that are compatible with Handler. Wouldn’t that work too?

The issue is that different middleware components would have different function signatures, making it impossible to chain them together. However, if Handler is defined as a standard library interface, middleware can accept and return Handler objects, allowing for seamless chaining.

One more interesting point: in GoLang, not only functions but any type can implement an interface. (GoLang truly treats everything as an object!)

Note: The post is authorized to republish and translated from https://www.kawabangga.com/posts/6903

GOLANG  ONER FUNCTION INTERFACE 

           

  RELATED


  0 COMMENT


No comment for this article.



  RANDOM FUN

How automation deployment works