-3

I'm learning Go coming from a PHP/JS background. I came across an example where I'm not quite sure what's happening. How is timeHandler receiving the http.ResponseWriter & http.Request if they are not explicitly passed in to the function call?

package main

import (
    "log"
    "net/http"
    "time"
)

func timeHandler(w http.ResponseWriter, r *http.Request) {
    tm := time.Now().Format(time.RFC1123)
    w.Write([]byte("The time is: " + tm))
}

func main() {
    mux := http.NewServeMux()

    // *** Why isn't there any undefined value errors here?
    th := http.HandlerFunc(timeHandler)

    mux.Handle("/time", th)

    http.ListenAndServe(":8080", mux)
}

The related post did not answer the question in layman enough terms. The thread in this post with Deefdragon did a good job. To summarize:

it registers the function, without calling it until later

Dan
  • 53
  • 1
  • 6
  • 3
    What do you mean? It is explicitly passed the request and response, they're the arguments to the handler function. – Adrian Feb 06 '20 at 15:00
  • 3
    See related:https://stackoverflow.com/questions/49668070/how-does-servehttp-work/49673552 – Adrian Feb 06 '20 at 15:01
  • 2
    @Adrian I think his confusion is how is the handler even called and who creates the response writer. Functions as first class objects might be new to the asker coming from PHP. – Ashhar Hasan Feb 06 '20 at 15:01
  • In the function signature they are defined, but they are not instantiated in `main()` prior to the function call and passed in. – Dan Feb 06 '20 at 15:02
  • 2
    You just register your `timeHandler` function, and the HTTP server launched by `http.ListenAndServe()` will call it, creating and passing response writer and request. – icza Feb 06 '20 at 15:03

2 Answers2

4

In go, functions are first class, meaning they can be assigned to variables, passed into functions etc.

In this case, http.HandlerFunc accepts as an argument a function (with parameters a writer and request), creating a handler th. th is then passed to the mux.

When a request is made to the time endpoint, the mux looks for the proper handler, th. It then executes th. (sometimes called a callback)

Inside th, the callback to the handler function is then made, passing the arguments w and r. This then executes the code in your example

deef0000dragon1
  • 357
  • 4
  • 17
  • So if I'm understanding right, it kinda registers the function, without calling it until later. When it is later called, it is then passed arguments. That sound about right? – Dan Feb 06 '20 at 15:36
1

By the source code from the documentation

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

As you can see it passes a function that must have a specific signature. It's not invoking anything it's simply using a pointer to it. The function will then be invoked by the package. So in order for the package to invoke it must know what signature to call.

You could think it like an interface. It's simply the declaration, the implementation is a different thing. The argument are passed automaticly by the http package when someone goes to your url.

A_kat
  • 1,459
  • 10
  • 17
  • Small note, the code I shared uses Handle**__r__**Func, with an "r" on the end – Dan Feb 06 '20 at 15:10
  • 1
    @DanielFoust y this is simply the name of the parameter. You can change however you like. ` func(ResponseWriter, *Request)` simply means that you have to pass a function with that declaration. `func timeHandler(testa http.ResponseWriter, request *http.Request)` would still work just fine. – A_kat Feb 06 '20 at 15:12
  • I wasn't referring to the parameter. The `http` function called was `HandlerFunc`, not `HandleFunc`. It's easy to miss – Dan Feb 06 '20 at 15:21