package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } }
zzh@ZZHPC:/zdata/MyPrograms/Go/testing$ go run main.go 0 0 1 -2 3 -6 6 -12 10 -20 15 -30 21 -42 28 -56 36 -72 45 -90
Go functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables.
For example, the adder
function returns a closure. Each closure is bound to its own sum
variable.
Why do you need closures? They have many uses, but writing middleware for web applications is one of the most well-known.
Web application middleware are functions that can be added to the web application’s request/response pipeline to handle common tasks like logging or access control. In Go, it’s common to implement web application middleware using closures.
To see how closure is being used, here’s a simple web application that shows “Hello World” when you access the web application at /hello:
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/hello", hello) http.ListenAndServe(":8000", nil) } func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello World") }
The web application is simple. It has a handler named hello , which is used as a parameter in the HandleFunc function to set up a handler for the web application.
If you want to log to screen or file each time a handler is called, and you also want to know how long the handler takes to execute its tasks, you could create a small function that allows you to log and call that function in each handler. It’s tedious, but it works. Logging the time, however, is even messier because you need to insert code at the beginning and run some deferred code to get the end timing to figure out the execution time.
Alternatively, you can create a piece of middleware that will do both:
package main import ( "fmt" "log" "net/http" "reflect" "runtime" "time" ) func main() { http.HandleFunc("/hello", logger(hello)) http.ListenAndServe(":8000", nil) } func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello World") } func logger(f http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { start := time.Now() f(w, r) end := time.Now() name := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() log.Printf("%s (%v)", name, end.Sub(start)) } }
Run main.go in a terminal, visit localhost:8000/hello in the browser, you'll see the log informaion added by the logger middleware function:
zzh@ZZHPC:/zdata/MyPrograms/Go/testing$ go run main.go 2023/10/01 09:10:41 main.hello (5.27µs)
func FuncForPC ¶
FuncForPC returns a *Func describing the function that contains the given program counter address, or else nil.
If pc represents multiple functions because of inlining, it returns the *Func describing the innermost function, but with an entry of the outermost function.