函数
定义
Go支持普通函数、匿名函数和闭包。
Go函数本身可以作为值传递,Go的函数支持多返回值
func functionName([parameters]) [returnTypes] {
body
}
示例:
package main
import "fmt"
func main() {
a := 1
b := 2
fmt.Printf("%dn", sum(a, b))
sum, multi := sumAndMulti(a, b)
fmt.Printf("sum = %d, multi = %d", sum, multi)
}
func sum(a, b int) int {
return a + b
}
func sumAndMulti(a, b int) (int, int) {
return a + b, a * b
}
Go函数本身可以作为值传递示例:
package main
import "fmt"
// 定义函数类型
type OperateFunc func(a, b int) int
func main() {
a := 10
b := 20
operator := "*"
result := 0
if operator == "+" {
result = operate(sum, a, b)
}
if operator == "-" {
result = operate(minus, a, b)
}
// 匿名函数
if operator == "*" {
result = operate(func(a, b int) int {
return a * b
}, a, b)
}
fmt.Println(result)
}
func sum(a, b int) int {
return a + b
}
func minus(a, b int) int {
return a - b
}
// 使用函数类型
func operate(fn OperateFunc, a, b int) int {
return fn(a, b)
}
函数返回值
函数返回值可以有多个,同时支持返回值命名。多个返回值会默认赋予类型零值。
示例:
package main
import "fmt"
func main() {
names, hashMap, age := fun1()
fmt.Println(names, hashMap, age)
}
func fun1() (names []string, hashMap map[string]int, age int) {
hashMap = make(map[string]int)
hashMap["gender"] = 1
return
}
// output
[] map[gender:1] 0
函数参数
函数参数有两种:
-
值传递,实际调用函数时,参数会复制一份,实际操作的内容是赋值的内存,不会影响原值 -
引用传递(地址传递),同样会复制一份地址值,所以实际操作会修改原值
经典例子交换两个数:
package main
import "fmt"
func main() {
a := 1
b := 2
swap(a, b)
fmt.Println(a, b)
swap2(&a, &b)
fmt.Println(a, b)
}
func swap(a, b int) {
a, b = b, a
}
func swap2(a, b *int) {
*a, *b = *b, *a
}
// output
1 2
2 1
Go也支持可变参数,只能有一个,且是最后一个,本质上是一个slice,参数传递时可以不用一个一个赋值,直接传递一个数组或者slice。
示例:
package main
import "fmt"
func main() {
fmt.Println(sum(1, 2, 3))
nums := []int{1, 2, 3}
// 如果是切片传递方式,要加...
fmt.Println(sum(nums...))
}
func sum(nums ...int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}
匿名函数
不带函数名的声明方式,可以在定义时直接调用匿名函数。
示例:
package main
import "fmt"
func main() {
fn1 := func(x int) int {
return x
}
fmt.Println(fn1(10))
func(x int) int {
fmt.Println(x)
return x
}(10)
}
// output
10
10
匿名函数也可以当作函数返回值。
示例:
package main
import "fmt"
func main() {
a := 10
b := 20
sumFunc, minusFun := operate()
fmt.Println(sumFunc(a, b))
fmt.Println(minusFun(a, b))
}
func operate() (func(a, b int) int, func(a, b int) int) {
sum := func(a, b int) int {
return a + b
}
minus := func(a, b int) int {
return a - b
}
return sum, minus
}
// output
30
-10
延迟调用
使用关键字defer
,特性:
-
直到return前才被执行,可以用来做资源清理(文件句柄释放、锁释放等) -
多个defer语句,按照先进后出顺序执行
示例:
package main
import "fmt"
func main() {
nums := []int{1, 2, 3}
for _, num := range nums {
defer fmt.Println(num)
}
}
// output
3
2
1
调用defer
会立刻拷贝函数中引用的外部参数,defer
的函数在压栈的时候也会保存参数的值,并非在执行时取值。
示例:
package main
import (
"log"
"time"
)
func main() {
start := time.Now()
log.Printf("start time:%vn", start)
defer log.Printf("diff:%vn", time.Since(start))
time.Sleep(3 * time.Second)
}
// output
2022/08/16 21:36:34 start time:2022-08-16 21:36:34.8768915 +0800 CST m=+0.001032201
2022/08/16 21:36:37 diff:4.6368ms
为了避免这种情况,可以把待执行语句放到匿名函数中。
package main
import (
"log"
"time"
)
func main() {
start := time.Now()
log.Printf("start time:%vn", start)
defer func() {
log.Printf("diff:%vn", time.Since(start))
}()
time.Sleep(3 * time.Second)
}
// output
2022/08/16 21:38:41 start time:2022-08-16 21:38:41.860342 +0800 CST m=+0.001027501
2022/08/16 21:38:44 diff:3.0190457s
异常处理
Go语言中通过panic
函数抛出异常,recover
函数捕获异常(只有在defer
调用的函数中才有效)。
panic
函数特性:
-
panic
会终止后续代码执行 -
等待 goroutine
退出时报告错误
recover
函数特性:
-
用来捕获 panic
,影响应用的行为
示例:
package main
import "fmt"
func main() {
defer func() {
if err := recover(); err != nil {
// interface{} 类型转具体类型
println(err.(string))
}
}()
panic("error!")
fmt.Println("success!")
}
// output
error!
defer
延迟调用中引发的错误,可以被后续延迟调用捕获,仅最后一个错误会被捕获。
示例:
package main
import "fmt"
func main() {
defer func() {
if err := recover(); err != nil {
// interface{} 类型转具体类型
println(err.(string))
}
}()
defer func() {
panic("defer error!")
}()
panic("error!")
fmt.Println("success!")
}
// output
defer error!
如果需要保护代码片段,出现错误后可以继续执行下方代码,可以通过匿名函数包装实现,如果出现错误,还是会继续执行匿名函数之后的代码(类比Java try catch
后不抛出只打印日志)
示例:
package main
import (
"fmt"
"log"
)
func main() {
func() {
defer func() {
if err := recover(); err != nil {
log.Println("error!")
}
}()
panic("出现了错误")
fmt.Println("继续执行!")
}()
fmt.Println("执行到代码结尾!")
}
// output
2022/08/16 22:07:21 error!
执行到代码结尾!
除了panic
用来引发中断错误外,还可以使用逻辑错误,返回内置的error
类型错误对象来表示函数调用状态。
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
标准库errors.New
和fmt.Errorf
函数用于创建实现error
接口的错误对象,通过具体的错误对象类型来确认错误类型。类比Java的异常定义。
示例:
package main
import (
"errors"
"fmt"
)
var ErrDivideByZero = errors.New("除0错误")
func main() {
defer func() {
fmt.Println(recover())
}()
switch result, err := divide(10, 0); err {
case nil:
println(result)
case ErrDivideByZero:
panic(err)
}
}
func divide(a, b int) (int, error) {
if b == 0 {
return 0, ErrDivideByZero
}
return a / b, nil
}
// output
除0错误
Go通过匿名函数实现类似try catch
的异常处理:
package main
import (
"errors"
"log"
)
var ErrDivideByZero = errors.New("除0错误")
func main() {
Try(func() {
// 正常执行代码
panic(ErrDivideByZero)
}, func(err interface{}) {
// 判断不同错误类型,执行不同逻辑
if err == ErrDivideByZero {
log.Println("Error:", err)
} else {
log.Println("Else Error:", err)
}
})
}
func Try(fun func(), handler func(interface{})) {
defer func() {
if err := recover(); err != nil {
handler(err)
}
}()
fun()
}
原文始发于微信公众号(erpang coding):Golang基础知识三
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/37405.html