赞
踩
- // 命名返回参数
- func add(a, b int) (sum int) {
- sum = a + b
- return // 这里不需要显式地写出返回值,因为已经在函数签名中声明了命名返回参数
- }
- package main
-
- import "fmt"
-
- //测试函数
- func test(x, y int, s string) (int, string) {
- n := x + y
- return n, fmt.Sprintf("%s,%d\n", s, n)
- }
- func main() {
- a, b := test(1, 2, "你好")
- // _可以忽略某些值的返回
- fmt.Println(a)
- fmt.Println(b)
-
- }

回调函数本质其实就是函数指针作为形参,传递给了函数,增加了代码的灵活度。
- package main
-
- import "fmt"
-
- // 回调函数1
- func testFunc(fn func() int) int {
- return fn()
- }
-
- // 定义函数类型
- type FormatFunc func(s string, x, y int) string
-
- func format(fn FormatFunc, s string, x, y int) string {
- return fn(s, x, y)
- }
-
- func formatHelper(s string, x, y int) string {
- return fmt.Sprintf(s, x, y)
- }
-
- func main() {
- s1 := testFunc(func() int {
- return 100
- })
- fmt.Println(s1) //100
-
- //匿名函数,回调进行格式化返回string
- //s2 := format(func(s string, x, y int) string {
- // return fmt.Sprintf(s, x, y)
- //}, "%d %d", 10, 20)
- s2 := format(formatHelper, "%d %d", 10, 20)
- fmt.Println(s2)
- }

闭包很简单,可以理解为返回值是一个函数指针,其他的再看就很好理解了。
https://juejin.cn/post/6844903793771937805
- package main
-
- import (
- "fmt"
- )
-
- //返回函数指针 func()int
- func a() func() int {
- i := 0
- b := func() int {
- i++
- fmt.Println(i)
- return i
- }
- return b
- }
-
- func main() {
- //a执行完返回func() int 的这个函数指针,其中赋值给c,
- //那么这里面保存有这个b匿名函数的所有信息,实现了自增,可以不用定义全局变量
- c := a()
- c()
- c()
- c()
- //因为这个是函数指针
- a() //不会输出i
- }

- package main
-
- import "fmt"
-
- // 递归1,求阶乘
- func Factorial(n int) int {
- if n <= 1 {
- return 1
- }
- return n * Factorial(n-1)
- }
-
- // 递归2,斐波那契数列
- func Fibonaci(n int) int {
- if n == 0 {
- return 0
- }
- if n == 1 {
- return 1
- }
- return Fibonaci(n-1) + Fibonaci(n-2)
- }
- func main() {
- fmt.Println("5!=", Factorial(5))
-
- fmt.Println("前10项斐波那契数列:")
- for i := 0; i < 10; i++ {
- fmt.Printf("%d\t", Fibonaci(i))
- }
- }

参考文档:异常处理 · Go语言中文文档
使用panic抛出错误,defer捕获错误,一般panic中抛出异常,defer中捕获异常,之后正常处理。
panic:
内置函数,panic后的代码不执行, interface{},直到goroutine整个退出并报告错误,
recover:
1.利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
2.recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
3.多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。
painc会导致程序直接退出,平时开发中尽量不随便使用。
一般场景:我的服务想要启动,必须依赖某些服务、日志、mysql能联通,配置文件没问题,那么才能启动的时候,直接使用panic
一旦服务启动,这时你的某行代码不小心触发panic,那么这就是重大事故(比如别人请求,你直接挂了)
但是架不住有些地方被动触发panic,这时就引入了recover来捕获panic
- package main
-
- import "fmt"
-
- // painc部分后面代码不执行
- func test() {
- defer func() {
- if err := recover(); err != nil {
- println("recover panic:", err.(string))
- }
- }()
- panic("panic错误测试!")
- //panic后的代码不执行
- //fmt.Println("panic后代码")
- }
-
- func main() {
- test()
- fmt.Println("main")
- }

- package main
-
- import (
- "errors"
- "fmt"
- )
-
- func A() (int, error) {
- return 2, errors.New("this is an error")
-
- }
-
- func main() {
- if _, err := A(); err != nil {
- fmt.Println(err)
- }
- }

recover需要延迟调用,也就是必须在defer的函数内部,否则返回nil
- package main
-
- import "fmt"
-
- func except() {
- fmt.Println("except延迟函数调用!")
- fmt.Println("except延迟函数recover:", recover())
- }
-
- func recoveryDemo() {
-
- //等效于下面的匿名延迟函数
- defer except()
-
- //延迟调用,recover在函数内部
- defer func() {
- if err := recover(); err != nil {
- fmt.Println("有效", err.(string))
- }
- }()
-
- //defer recover() //无效,不是延迟调用,nil
- defer fmt.Println(recover()) //无效,空
-
- defer func() {
- func() {
- fmt.Println("defer inner")
- fmt.Println("defer inner", recover()) //无效
- }()
-
- }()
-
- panic("panic错误测试!")
- //不会执行
- fmt.Println("End of test!")
- }
-
- func main() {
- recoveryDemo()
- fmt.Println("main")
- }

总结:需要recover捕获panic时defer延迟函数进行接受,并且第一个有效的recover只能捕获最后一个painc(如果多个panic),之后有效的recover也返回nil。
defer延迟调用,一般释放资源和连接、关闭文件、释放锁等等。类似于java的finally和c++中析构函数,不过defer一般跟在函数或方法中。
参考博客:【Golang】Go语言defer用法大总结(含return返回机制)_golang defer return-CSDN博客
多个defer满足后进先出
- package main
-
- import "fmt"
- //无返回值
- func test(a int) {
- defer fmt.Println("defer1 = ", a)//方法
- defer func(v int) {
- fmt.Println("defer2 = ", v)
- }(a)//有参函数
- defer func() {
- fmt.Println("defer3 = ", a)
- }()//无参函数
- a += 100
- }
-
- func main() {
- test(0)
- }

defer满足后进先出,其次,有参情况下a会先传递进入,最后等a+=100之后执行完了再输出。
可读取函数返回值(return返回机制)
先return结构写入返回值,后defer收尾,最后携带返回值退出.
无名返回值,有名返回值的区别,见下面demo
函数返回值可以无名、有名,这个是方便理解的不全代码,有名res的话本质局部变量,因此defer后会可能会影响res的返回值。而int这个返回值就直接定了。这个很容易引起bug,因此看下面例子:
- package main
-
- import "fmt"
-
- // 无名返回值
- func UnNamed(n int) int {
- n += 100
-
- defer func() {
- n += 100
- fmt.Println("UnNamed defer1", n)
- }()
-
- return n
- }
-
- // 有名返回值
- func Named(n int) (res int) {
- res = n + 100
-
- defer func() {
- res += 100
- fmt.Println("UnNamed defer1", res)
- }()
- // 返回res局部变量,因此受defer中的res的逻辑影响
- return res
- }
-
- func main() {
- n := 0
- fmt.Println("main UnNamed return:", UnNamed(n))
-
- fmt.Println("main Named return:", Named(n))
- }

对于第一无名返回值,先执行return保存返回值100,之后defer输出200,最后返回到main函数为100.
第二有名返回值,先执行return知道返回的是res(此时100),之后defer修改输出res200,最终返回到main为200.
同理可以更复杂,defer可以传入形参的无名有名函数,可以进行分析。
- package main
-
- import "fmt"
-
- // 无名返回值
- func UnNamed(n int) int {
- n += 100
-
- defer func(n int) { //传入100,输出110
- n += 10
- fmt.Println("UnNamed defer2", n)
- }(n)
-
- defer func() { //200
- n += 100
- fmt.Println("UnNamed defer1", n)
- }()
-
- return n //100
- }
-
- // 有名返回值
- func Named(n int) (res int) {
- res = n + 100 //100
-
- defer func(res int) { //传入100并且注意是值拷贝,并且入栈,110
- res += 10
- fmt.Println("UnNamed defer2", res)
- }(res)
-
- defer func() { //入栈
- res += 100
- fmt.Println("UnNamed defer1", res)
- }()
-
- return res //100->200
- }
-
- func main() {
- n := 0
- fmt.Println("main UnNamed return:", UnNamed(n)) //100
-
- fmt.Println()
- fmt.Println("main Named return:", Named(n)) //200
- }

因此传入指针等等,defer函数,有无名返回值均会影响main函数中接收到的最终return的值,请注意。
nil代表某些数据类型的零值
bool false
number 0
string ""
slice、map、channel、pointer、interface{} nil
如果是结构体,那么它的零值是,内部所有属性的零值的集合
这里分析了slice、map中的nil和empty的区别。
- package main
-
- import "fmt"
-
- type Student struct {
- name string
- age int
- }
-
- func main() {
- // nil slice,其实可以创建,append对nil slice进行了处理,但是map就不行
- var s1 []Student
- //s1 = append(s1, Student{"Bob", 19})
- fmt.Println(s1)
- if s1 == nil {
- fmt.Println("s1==nil")
- }
-
- // 不是nil slice,其实本质上是创建了,内部ptr指向一个空间为0的数组
- var s2 = make([]Student, 0)
- if s2 == nil {
- fmt.Println("s2==nil")
- }
-
- // nil map
- var m1 map[int]Student
- if m1 == nil {
- fmt.Println("m1==nil")
- }
- //可以查询,但是无法添加键值对,panic(assignment to entry in nil map)
- //m1[1] = Student{"hhh", 123}
- //if val, ok := m1[1]; ok {
- // fmt.Println("ok", val)
- //} else {
- // fmt.Println("not ok")
- //}
- fmt.Println(m1)
-
- //不是nil map,已经初始化了,0个空间的map
- var m2 = make(map[int]Student, 0)
- //可以查询,插入数据了
- m2[1] = Student{"hhh", 123}
- if val, ok := m2[1]; ok {
- fmt.Println("ok", val)
- } else {
- fmt.Println("not ok")
- }
- fmt.Println(m2)
-
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。