15.Go语言-函数是“一等公民”

280 阅读1分钟

本文视频地址

一 函数可以怎么用

1 在函数内创建
// $GOROOT/src/runtime/print.go
func hexdumpWords(p, end uintptr, mark func(uintptr) byte) {
        p1 := func(x uintptr) {
                var buf [2 * sys.PtrSize]byte
                for i := len(buf) - 1; i >= 0; i-- {
                        if x&0xF < 10 {
                                buf[i] = byte(x&0xF) + '0'
                        } else {
                                buf[i] = byte(x&0xF) - 10 + 'a'
                        }
                        x >>= 4
                }
                gwrite(buf[:])
        }
    ...
}

在 hexdumpWords 函数内部,变量 p1被匿名函数赋值。

2 作为类型

使用函数来自定义类型

// $GOROOT/src/net/http/server.go
type HandlerFunc func(ResponseWriter, *Request)
3 存储到变量中
// $GOROOT/src/runtime/vdso_linux.go
func vdsoParseSymbols(info *vdsoInfo, version int32) {
        if !info.valid {
                return
        }

        apply := func(symIndex uint32, k vdsoSymbolKey) bool {
                sym := &info.symtab[symIndex]
                typ := _ELF_ST_TYPE(sym.st_info)
                bind := _ELF_ST_BIND(sym.st_info)
        ... 
                *k.ptr = info.loadOffset + uintptr(sym.st_value)
                return true
        }
    ...
}
4 作为参数传入函数
$GOROOT/src/time/sleep.go

func AfterFunc(d Duration, f func()) *Timer {
        t := &Timer{
                r: runtimeTimer{
                        when: when(d),
                        f:    goFunc,
                        arg:  f,
                },
        }
        startTimer(&t.r)
        return t
}
5.作为返回值从函数返回
// $GOROOT/src/strings/strings.go
func makeCutsetFunc(cutset string) func(rune) bool {
        if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
                return func(r rune) bool {
                        return r == rune(cutset[0])
                }
        }
        if as, isASCII := makeASCIISet(cutset); isASCII {
                return func(r rune) bool {
                        return r < utf8.RuneSelf && as.contains(byte(r))
                }
        }
        return func(r rune) bool { return IndexRune(cutset, r) >= 0 }
}

二 函数作为“一等公民”的特殊运用

1. 像整型变量那样对函数进行显式转型
func Welcome(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, “欢迎来到Go语言的世界!\n")
}                    

func main() {
        http.ListenAndServe(":8080", http.HandlerFunc(Welcome))
}


// $GOROOT/src/net/http/server.go
func ListenAndServe(addr string, handler Handler) error {
        server := &Server{Addr: addr, Handler: handler}
        return server.ListenAndServe()
}

可以看到ListenAndServe方法的第二个参数 handler ,而这里 handler 参数的类型 http.Handler 接口。

// $GOROOT/src/net/http/server.go
type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

http.Handler 接口只有一个ServeHTTP方法,我们可以看到它的原型是func(http.ResponseWriter, *http.Request),与Welcome原型一模一样。

// $GOROOT/src/net/http/server.go

type HandlerFunc func(ResponseWriter, *Request)

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

HandlerFunc实现了ServeHTTP方法,也就实现了Handler接口,函数 Welcome 显式转换为 HandlerFunc 类型。