函数声明与函数变量

  1. 每个函数声明都包含一个名字、一个可选的返回值列表以及函数体。当函数返回一个未命名的返回值或者没有返回值的时候,返回列表的圆括号可以省略
  2. 返回值也可以像形参一样命名,这时每一个命名函数的返回值会声明为一个局部变量,并根据变量类型初始化为相应的0值
  3. 函数的类型被称为函数签名,当两个函数拥有相同的形参列表和返回值列表时,认为这两个函数的类型或签名是相同的,形参和返回值的名字不会影响到函数类型
  4. 实参默认是按值传递的,函数接收到的是每个实参的副本,如果传递的实参包含引用类型,比如指针、slice、map、函数或者通道,那么当函数使用形参变量时就有可能间接地修改实参变量
  5. 许多编程语言使用固定长度的函数调用栈,相比固定大小的栈,Go语言的实现使用了可变长度的栈,随着使用而增长,可达到1GB左右的上限
  6. 一个函数能够返回不止一个结果,返回一个多值结果可以是调用另一个多值返回的函数
  7. 一个多值调用可以作为单独的实参传递给拥有多个形参的函数中,良好的名称可以使得返回值更加有意义
  8. 裸返回是将每个命名返回结果按照顺序返回的快捷方法,但是不能使代码更加易于理解
  9. 如果当函数调用发生错误时返回一个附加的结果作为错误值,通常将错误值作为最后一个结果返回,如果错误只有一种情况结果通常设置为布尔类型
  10. Go通过使用普通的值而非异常来报告错误,一般当一个函数返回一个非空错误时,它其他结果都是未定义的而且应该忽略的。Go使用通常的控制流机制(比如if和return语句)应对错误
  11. 错误处理策略:
    (1)将错误传递下去,使得在子例程中发生的错误变为主调例程的错误
    (2)对于不固定或是不可预测的错误,在短暂的间隔后对操作进行重试似乎是合理的,超出一定的重试次数和限定时间后再报错退出
    (3)如果依旧不能顺序进行下去,调用者能够输出错误然后优雅的停止程序,这样的处理应该留给主程序部分
    (4)在一些错误下,只记录下错误信息然后程序继续运行
  12. io包保证任何由文件结束引起的读取错误,始终都将会得到一个与众不同的错误——io.EOF,io.EOF有一条固定的错误消息“EOF”
  13. 函数变量可以像其他函数一样调用,零值是nil(空值),调用一个空的函数变量将导致宕机
  14. 函数变量可以和空值相比较,但是它们本身不可比较,所以不可以互相比较或者作为键值出现在map中

匿名函数和延迟函数调用

  1. 命名函数只能在包级别的作用域进行声明,但我们能够使用函数字面量在任何表达式内指定函数变量,func关键字后面没有名称,是一个表达式,值称作匿名函数
  2. 以匿名函数方式定义的函数能够获取到整个的词法环境,因此里层的函数可以使用外层函数中的变量,里层的匿名函数能够获取和更新外层函数的局部变量,这些隐藏的变量引用就是我们把函数归类为引用类型而且函数变量无法进行比较的原因,所以这些变量的生命周期不是由作用域所决定的
  3. 当一个匿名函数需要递归时,必须先声明一个变量然后将匿名函数赋给这个变量,如果将这两个步骤合并成一个声明,函数字面量将不能存在于函数变量的作用域中,这样也就不能递归地调用自己了
  4. 循环内使用匿名函数时需要注意,如果匿名函数中使用了循环变量,需要在循环体内将循环变量赋给一个新的局部变量,而不是直接使用循环变量。因为在循环内创建的所有函数变量共享相同的变量——一个可访问的存储位置,而不是固定的值,而函数变量内循环变量的取值永远是最后一个值
  5. 变长函数被调用时可以有可变的参数个数,在参数列表最后的类型名称前面使用省略号“…”表示声明一个变长的函数
  6. 变长函数体内,可变参数是一个slice,相当于调用者显式地申请一个数组,将实参赋给这个数组,并把数组slice传递给函数
  7. 当函数实参已经存在于一个slice中的时候,可以在最后一个参数后面放一个省略号就可以调用一个变长函数
  8. 尽管变长参数像函数体内的slice,但是变长函数的类型和一个带有普通slice参数的函数的类型不相同
  9. 一个defer语句就是在一个普通的函数或方法调用之前加上defer关键字,defer语句的执行会推迟到包含defer语句的函数结束后才执行,defer语句没有限制使用次数,执行的时候以调用defer语句顺序的倒序进行
  10. defer语句经常用于成对的操作,比如打开和关闭,连接和断开等,正确使用defer语句的地方是在成功获得资源之后
  11. defer语句函数和参数表达式会在语句执行时求值,实际的调用推迟到包含defer语句的函数结束后才执行,因此defer语句可以在函数的“入口”和“出口”行为处设置调试行为以调试一个复杂的函数
  12. 延迟执行的匿名函数能够改变外层函数返回给调用者的结果

宕机

  1. 当Go语言运行时检测到错误时就会发生宕机行为,一个典型的宕机行为发生时,正常的程序执行会终止,goroutine中的所有延迟函数会执行,然后程序异常退出并留下一条日志消息
  2. 不是所有的宕机都在运行时发生,可以直接调用内置的宕机函数,这个函数可以接受任何值作为参数,如果遇到不可能发生的情况,宕机是最好的处理方式
  3. 当宕机发生时,所有的延迟函数以倒序执行,从栈最上面的函数开始一直返回main,go语言的宕机机制让延迟执行的函数在栈清理之前调用
  4. 如果内置的recover函数在延迟函数的内部调用,而且这个包含defer语句的函数发生宕机,recover会终止当前的宕机状态并且返回宕机的值。函数不会从宕机的地方继续运行而是正常返回,如果recover在其他任何情况下运行则它没有任何效果且返回nil
  5. 从一个包内发生的宕机进行恢复有助于简化处理复杂和未知的错误,但一般原则是不应该尝试去恢复从另一个包内发生的宕机
  6. 可以通过使用一个明确的、非导出类型作为宕机值,之后检测recover的返回值是否是这个类型,如果是这个类型可以像普通的error那样处理宕机,否则使用同一个参数继续处理宕机
  7. 有些情况下是没有恢复动作的,比如内存耗尽使得go运行时发生严重错误而直接终止进程