一、函数定义

函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。

    func name(parameter-list) (result-list) {
        body
    }
    func add(x int, y int) int {return x + y}
    func sub(x, y int) (z int) { z = x - y; return}
    func first(x int, _ int) int { return x }
    func zero(int, int) int { return 0 }
    fmt.Printf("%T\n", add) // "func(int, int) int"
    fmt.Printf("%T\n", sub) // "func(int, int) int"
    fmt.Printf("%T\n", first) // "func(int, int) int"
    fmt.Printf("%T\n", zero) // "func(int, int) int"

二、递归

函数可以调用函数自身。

    func f(n int) int {
        if n == 1 || n == 2 {
            return 1
        }
        return f(n-1) + f(n-2)
    }

三、多返回值函数

Go可以返回多个值给调用者
一般返回数据信息和错误信息

    func calc(x, y int) (int, int) {
        return x + y, x * y
    }

四、错误

函数执行时难免产生错误。比如通常IO操作很容易产生错误。

  1. 错误处理策略
    (1) 传播错误:将错误传给上层调用者处理
    func ...{
        resp, err := http.Get(url)
        if err != nil{
            return nill, err
        }
    }

(2) 偶然性的错误,采用尝试有限次数的重试策略

    // WaitForServer attempts to contact the server of a URL.
    // It tries for one minute using exponential back-off.
    // It reports an error if all attempts fail.
    func WaitForServer(url string) error {
        const timeout = 1 * time.Minute
        deadline := time.Now().Add(timeout)
        for tries := 0; time.Now().Before(deadline); tries++ {
            _, err := http.Head(url)
            if err == nil {
                return nil // success
            }
            log.Printf("server not responding (%s);retrying…", err)
            time.Sleep(time.Second << uint(tries)) // exponential back-off
        }
        return fmt.Errorf("server %s failed to respond after %s", url, timeout)
    }

(3) 输出错误信息,结束程序。一般在main函数中执行的策略。

    func main() {
        fmt.Println("before log fatal")
        log.Fatalf("Site is down\n")    //exit 
        fmt.Println("after log fatal")
    }

(4) 只输出错误信息,不做其它处理

    if err := Ping(); err != nil {
        log.Printf("ping failed: %v; networking disabled",err)
    }

(5) 忽略掉错误

  1. 文件结尾错误(EOF)
    in := bufio.NewReader(os.Stdin)
    for {
        r, _, err := in.ReadRune()
        if err == io.EOF {
        break // finished reading
    }
    if err != nil {
        return fmt.Errorf("read failed:%v", err)
    }
    // ...use r…
    }

五、函数值

在Go中,函数被看作第一类值(first-class values):函数像其他值一样,拥有类型, 可以被赋值给其他变量,传递给函数,从函数返回。对函数值(function value)的调用 类似函数调用。 函数值不可以作map的key。 函数值的默认零值为nil。

    func square(n int) int { return n * n }
    func negative(n int) int { return -n }
    func product(m, n int) int { return m * n }
    f := square
    fmt.Println(f(3)) // "9"
    f = negative
    fmt.Println(f(3)) // "-3"
    fmt.Printf("%T\n", f) // "func(int) int"
    f = product // compile error: can't assign func(int, int) int to func(int) int

六、匿名函数

函数值字面量是一种表达式,它的值被称为匿名函数(anonymous function)。

    // squares返回一个匿名函数。
    // 该匿名函数每次被调用时都会返回下一个数的平方。
    func squares() func() int {
        var x int
        return func() int {
            x++
            return x * x
        }
    }
    func main() {
        f := squares()
        fmt.Println(f()) // "1"
        fmt.Println(f()) // "4"
        fmt.Println(f()) // "9"
        fmt.Println(f()) // "16"
    }
    //解析: f := squares()执行后,
    // f = func() int { x++; return x * x }
    // 函数 + 引用环境 = 闭包

七、可变参数

参数数量可变的函数称为为可变参数函数。 在参数列表的最后一个参数类型之前加上省略符号“…”,这表示 该函数会接收任意数量的该类型参数。

    func sum(vals...int) int {
        total := 0
        for _, val := range vals {
        total += val
    }
        return total
    }

    // 在函数体中,vals被 "看作" 是类型为[]int的切片
    fmt.Println(sum()) // "0"
    fmt.Println(sum(3)) // "3"
    fmt.Println(sum(1, 2, 3, 4)) // "10"

    // 若传入参数已经为[]int 切片,只需
    // 在最后一个参数后加上省略符。
    values := []int{1, 2, 3, 4}
    fmt.Println(sum(values...)) // "10"

    //实际上切片参数函数和可变参数函数是不同的函数
    func f(...int) {}
    func g([]int) {}
    fmt.Printf("%T\n", f) // "func(...int)"
    fmt.Printf("%T\n", g) // "func([]int)"

八、Deferred函数

  1. defer 在函数执行完毕时,才执行 defer后面的语句或函数
  2. defer多用于成对的操作,如打开和关闭、连接和断开、加锁和解锁等
  3. 即使defer作用域中语句执行异常,defer后面的语句也会被执行
    var mu sync.Mutex
    var m = make(map[string]int)
    func lookup(key string) int {
        mu.Lock()
        defer mu.Unlock()
        return m[key]
    }

九、Panic异常

Panic 用于处理严重的错误 任何能够被预料的错误,都应该被优雅的处理,而不用Panic

    func Reset(x *Buffer) {
        if x == nil {
            panic("x is nil") // unnecessary!
        }
        x.elements = nil
    }

十、Recover捕获异常

通常来说,不应该对panic异常做任何处理,但有时,也许我们可以从异常中恢复,至少我们 可以在程序崩溃前,做一些操作。

    func Parse(input string) (s *Syntax, err error) {
        defer func() {
            if p := recover(); p != nil {
                err = fmt.Errorf("internal error: %v", p)
            }
        }()
        // ...parser...
    }