#《Go 语音圣经(中文版)》

虽然Go的垃圾回收机制会回收不被使用的内存,但是这不包括操作系统层面的资源,比如打开的文件、网络连接。因此我们必须显式的释放这些资源。

如果一个函数所有的返回值都有显式的变量名,那么该函数的return语句可以省略操作数。

不宜过度使用bare return

error类型可能是nil或者non-nilnil意味着函数运行成功,non-nil表示失败。

对于non-nilerror类型,我们可以通过调用errorError函数或者输出函数获得字符串类型的错误信息。

通常,当函数返回 non-nil 的 error 时,其他的返回值是未定义的(undefined),这些未定义的返回值应该被忽略。然而,有少部分函数在发生错误时,仍然会返回一些有用的返回值。比如,当读取文件发生错误时,Read 函数会返回可以读取的字节数以及错误信息。对于这种情况,正确的处理方式应该是先处理这些不完整的数据,再处理错误。因此对函数的返回值要有清晰的说明,以便于其他人使用。

四种错误处理方式:

  1. 传播错误。这意味着函数中某个子程序的失败,会变成该函数的失败。
  2. 重试。如果错误的发生是偶然性的,或由不可预知的问题导致的。一个明智的选择是重新尝试失败的操作。在重试时,我们需要限制重试的时间间隔或重试的次数,防止无限制的重试。
  3. 输出错误信息并结束程序。如果错误发生后,程序无法继续运行,我们就可以采用第三种策略:输出错误信息并结束程序。需要注意的是,这种策略只应在 main 中执行。对库函数而言,应仅向上传播错误,除非该错误意味着程序内部包含不一致性,即遇到了 bug,才能在库函数中结束程序。
  4. 输出错误信息。程序继续运行。
  5. 忽略错误

log中的所有函数,都默认会在错误信息之前输出时间信息。

Go中,错误处理有一套独特的编码风格。检查某个子函数是否失败后,我们通常将处理失败的逻辑代码放在处理成功的代码之前。如果某个错误会导致函数返回,那么成功时的逻辑代码不应放在else语句块中,而应直接放在函数体中。Go中大部分函数的代码结构几乎相同,首先是一系列的初始检查,防止错误发生,之后是函数的实际逻辑。

#函数值

函数像其他值一样,拥有类型,可以被赋值给其他变量,传递给函数,从函数返回。对函数值(function value)的调用类似函数调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

/*
@Time : 2021/5/20 20:29
@Author : onns
@File : ch5/5.1.go
*/

func square(n int) int { return n * n }
func negative(n int) int { return -n }
func product(m, n int) int { return m * n }

func main() {
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
}
1
2
3
4
[20:30:24] onns@Onns ~/Onns/code/go/go-bible/ch5 $ go run 5.1.go
9
-3
func(int) int

函数类型的零值是nil。调用值为nil的函数值会引起panic错误。

函数值可以与nil比较:

1
2
3
4
var f func(int) int
if f != nil {
f(3)
}

但是函数值之间是不可比较的,也不能用函数值作为mapkey