4.1 定义
使用关键字 func
定义函数。函数有一下特点:
- 无须前置声明
- 不支持命令嵌套定义
- 不支持同名函数重载
- 不支持默认参数
- 支持不定长变参
- 支持多返回值
- 支持命名返回值
- 支持匿名函数和闭包
函数属于第一类对象,具备相同签名(参数以及返回值列表)的视作同一类型
func main() {
f := hello
exec(f)
}
func hello() {
println("hello, world!")
}
func exec(f func()) {
f()
}
从阅读和代码维护的角度,使用命令类型更加方便
//定义函数类型
type FormatFunc func(string, ...interface{}) (string, error)
//如果不使用命令类型,这个参数签名会长到没法看
func format(f FormatFunc, s string, a ...interface{}) (string, error) {
return f(s, a...)
}
建议命名规则
在避免冲突的情况下,函数命名要本着精简短小、望文知意的原则。
-
通常是动词和介词加上名词,例如scanWords。
-
避免不必要的缩写,printError 要比printErr更好一些。
-
避免使用类型关键字,比如buildUserStruct看上去会很别扭。
-
避免歧义,不能有多种用途的解释造成误解。
-
避免只能通过大小写区 分的同名函数。
-
避免与内置函数同名,这会导致误用。
-
避免使用数字,除非是特定专有名词,例如UTF8。
-
避免添加作用域提示前缀。
-
统一使用camel/pascal case拼写风格。
-
使用相同术语,保持一 致性。
-
使用习惯用语,比如init表示初始化,is/has返回布尔值结果。
-
使用反义词组命名行为相反的函数,比如get/set. min/max等。
4.2 参数
Go不支持默认值的可选参数,不支持命令实参。调用时,必须按照签名顺序传递指定类型和数量的实参。
在参数列表中,相邻对的同类型参数可以合并。
func test(x, y int, s string, _ bool) *int {
return nil
}
参数可视作函数局部变量,因此不能再相同层次定义同名变量
func add(x, y int) int {
x := 10 //错误语法
var y int //错误语法
return x * y
}
形参是函数定义中的参数,实参是函数调用时所传递的和参数。形参类似函数局部变量,实参则是函数的外部对象。可以是常量、变量、表达式或者函数等。
不管是指针、引用类型,还是其他参数类型,都是值拷贝传递。区别无非是拷贝目标对象,还是拷贝指针。在函数调用前,会为形参和返回值分配内存空间,并将实参拷贝到形参内存。
变参
变参本质就是一个切片。只能接收到一个到多个同类型的参数,且必须放到列表的尾部。
func main() {
test("abc",1, 2, 3, 4)
}
func test(s string, a ...int) {
fmt.Printf("%T,%v\n", a, a)
}
//运行结果
[]int,[1 2 3 4]
4.3 返回值
有返回值的函数,必须有明确的return终止语句。除非有panic,或者无break的死循环,则无须终止语句。可以使用_
忽略不想要的返回值。
可以有多个返回值,又是error模式
func div(x, y int) (int, error){
if y == 0 {
return 0, errors.New("division by zero")
}
return x / y , nil
}
命名返回参数
func paing(sql string, index int) (count int, pages int, err error)
命令返回参数和参数一样,可以当做函数局部变量使用,最后由return
隐式返回。
func div(x, y int) (z int, err error){
if y == 0 {
err = errors.New("division by zero")
return
}
z = x / y
return //相当于 "return z, nil"
}
4.4 匿名函数
匿名函数是指没有定义名字符号的函数。除了没有名字,匿名函数和普通函数完全一样。最大的区别是我们可以再函数内部定义匿名函数,形成类似嵌套的效果。
func main() {
func(s string){
println(s)
}( "hello")
}
闭包
闭包是指上下文中引用了自由变量的函数,或者说是函数和其引用的环境的组合体。
4.5 延迟调用
语句defer
向当前函数注册稍后执行的函数调用。这些调用被称作延迟调用,因为他们直到当前函数执行结束前才被执行。常用于资源释放、解除锁定以及错误处理等操作。
func main() {
f, err := os.Open("./main.go")
if err != nil {
log.Fatalln(err)
}
defer f.Close()
... do something ...
}
4.6 错误处理
error
官方推荐的标准做法是返回error
状态
func Scanln(a....interface{}) (n int, err error)
标注库将error定义为接口类型,以便实心自定义错误类型。
type error interface {
Error() string
}
按照惯例,error
总是最后一个返回参数。标准库提供了相关创建函数,可方便地创建包含简单错误文本的error
对象
var errDivByZero = errors.New("division by zero")
func main() {
z, err := div(5, 0)
if err == errDivByZero {
log.Fatalln(err)
}
println(z)
}
func div(x, y int) (int, error){
if y == 0 {
return 0, errDivByZero
}
return x / y , nil
}
建议:除非是不可恢复、导致系统无法正常工作的错误,否则不建议使用
painc