Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go function typing

Tags:

function

types

go

I'm confused about this bit of code from the HTTP package:

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

Why does the ServeHTTP method have the exact same signature as it's type - what's the point?

 

Testing, I discovered that if I pass a random function (foo) to the HandlerFunc:

var bar = HandlerFunc(foo)

bar becomes an instance of HandlerFunc with foo as its ServeHTTP method. Now I'm really confused about how on earth this works.

If I have more than one methods on a type, how do I know which one is going to be attached to the new instance and with what name or order?

like image 741
thwd Avatar asked Feb 22 '23 23:02

thwd


1 Answers

This approach allows you to use a function in a context that's expecting a Handler.

What happens is, there's a Handler interface:

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

and various functions are declared to accept parameters that are declared to belong to this interface — for example:

    func TimeoutHandler(h Handler, ns int64, msg string) Handler {
        f := func() <-chan int64 {
            return time.After(ns)
        }
        return &timeoutHandler{h, f, msg}
    }

What this means is that when you invoke such a function, you have to pass in an object belonging to a type that satisfies this interface, which is to say, a type that has a ServeHTTP method with the appropriate signature. (In Go, unlike some languages, a type doesn't need to explicitly implement an interface, it just needs to have the methods specified by the interface.)

So, the code-snippet that you quote:

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

creates a type HandlerFunc that's based on func(ResponseWriter, *Request), but augments the type with a method called ServeHTTP with an appropriate signature, so that it satisfies the Handler interface. This ServeHTTP method just calls the function itself. So, if f is a function with the right signature, you could write something like this:

var h HandlerFunc = f    // h == f, but converted to a HandlerFunc
                         // so it satisfies the Handler interface.
TimeoutHandler(h, 1000000, "timed out")

To clarify a few things about this:

Testing, I discovered that if I pass a random function (foo) to the HandlerFunc:

var bar = HandlerFunc(foo)

bar becomes an instance of HandlerFunc with foo as its ServeHTTP method. Now I'm really confused about how on earth this works.

Firstly, it's more correct to say that you've converted a random function foo to type HandlerFunc, rather than that you've passed the function to HandlerFunc as though HandlerFunc were a function. (The HandlerFunc(foo) notation is a typecast; you could just as well write var bar HandlerFunc = foo and let the conversion happen implicitly.)

Secondly, it's more correct to say that bar has a ServeHTTP method that invokes foo, than that foo itself actually is the ServeHTTP method.

Does that make sense?

like image 167
ruakh Avatar answered Feb 26 '23 22:02

ruakh