函数

函数的定义

普通函数
函数定义有 func关键字,然后函数名,接着是传递的参数(是值传递,非引用传递),然后是返回类型。

func test(a,b int,c string)(int,string,bool){
    return a + b,c,(a+b)>5
}
func main () {
    a,b,c := test(5,2,"ccc")
    fmt.Println(a,b,c)
}

//输出 7 ccc true

可变参数

return 没写返回值时,默认按返回参数格式去返回

func test(a,b int)(sum int,err error){
    sum = a+b
    return 
}
func test(item ... int)(sum int,err error){
    fmt.Println(item)
    return 
}
func main () {
    aa  := 2
    a ,b:= test(1,aa)
    fmt.Println(a,b)
}
输出
[1 2]
0 

函数一等公民特性

可以当作变量来用,我们下面例子用 function这个变量 来赋值成test这个函数,然后用function来调用test函数。当变量来用时,赋值时不带括号,带括号等于使用了,此时变量变成接收返回值了。

func test(item ... int)(sum int,err error){
    fmt.Println(item)
    return 
}
func main () {
    function := test
    aa  := 2
    a ,b:= function(1,aa)
    fmt.Println(a,b)

返回函数,在cals这个里面返回一个函数。如果有多传参数也可以在返回的函数里面处理

func cals(op string) func() {

    if op == "+" {
        return func() {
            fmt.Println("这是加法")
        }

    } else {
        return func() {
            fmt.Println("其他正在开发")
        }

    }

}
func main() {
    cals("+")()
}
输出
这是加法

传入函数 匿名函数

func cals(one int ,myfunc func() int) int{


    return one +  myfunc()
}
func main () {
   a :=1
   b:=2
   num := cals( 5,func ( )int{

    return a + b
   })
   fmt.Println(num)
    输出
    8
    //cals 传入2个参数,1个是one,我们传的5,另一个是我们自定义的func,func里面计算了a + b 也就是3。 所以就是 5 + 3(1+2) = 8

}

第二种方式 传入普通函数

func add(a , b int) (sum int){
    return  a+b
}
func cals(a int ,myfun c func(a int,b int) int) int{
    return myfunc(a,5)
}
func main () {
   fmt.Println(cals(3,add))
}

//输出 8

把上面的普通函数改成匿名函数

func cals(a int ,myfun c func(a int,b int) int) int{

    return myfunc(a,5)
}
 fmt.Println(cals(1,func(a , b int) int{ 
       return a + b
       }))

匿名函数传入变量在传进来也可以


func cals(a int ,myfunc func(a int,b int) int) int{
    return myfunc(a,5)
}
func main () {

   addfunc := func(a , b int) int{ 
       return a + b
       }
   fmt.Println(cals(1,addfunc))
}

闭包的特性

闭包函数可以访问上一层的数据。 例如aotoIncrement 里面的闭包是可以访问到inc的。

这个的意思是我们 x := autoIncrement() x获取到aoto的实力并初始化了inc的变量。
然后返回的是匿名函数,调用一次匿名函数把auto的 inc + 1。但不会从新初始化inc = 0,除非我们再次调用,例如i ==3时我们再次调用。


func autoIncrement() func() int {
    inc := 0;
    return func() int{
        inc +=1
        return inc
    }
}
func main () {

  x := autoIncrement()

  for i :=0;i<10;i++{
      fmt.Println(x())
      if i ==3{
           x = autoIncrement()
      }
  }
}

输出
1
2
3
4
1
2
3
4
5
6

defer的应用场景

程序有些东西打开后,忘记关掉会有问题。但是打开又不能马上关可以用这个。。
例如我们要写文件或mysql链接时流程应该是,打开文件(获取锁),写数据,关闭文件(释放锁)
但是如果我们写数据逻辑如果N长,如果100行,或者会忘了。

我们可以用defer,函数执行完完回调(在return前执行)。栈方式执行

    defer fmt.Println("1")
    defer fmt.Println("2")
    fmt.Println(3)
    //输出
    3
    2
    1

以上面例子,输出3,然后defer后进先出的栈原理,输出2,在1。

func defret() (ret int ){
    defer func (){
        ret +=5
    }()
    return 
}
func main () {
    fmt.Println(defret())
}
//输出 5

上面例子输出5,因为调用defret时,遇到return,执行时他执行defer调用匿名函数修改ret

error设计理念

其他语言都是try catch,go不行,要求我们必须处理error,所以代码会出现大量 if err != nil
这属于防御性编程,这种代码健壮性好,但写代码烦人。。

import "errors"

func A()(int,error){
    return 1,errors.New("出错了")
}
func main () {
    if _,err:= A(); err!= nil{
        fmt.Println(err)
    }
}
//第一个返回值我们用_接收掉了,可以不打印,第二个值我们打印err的数据

使用panic&panic

panic 会使程序退出,但会导致程序退出,不推荐用。。一般如果需要调用某些依赖时,才会用到这个抛出。
比如 调用某些依赖,mysql是否能连通,或者配置文件是否正确等才会用这个去抛出。。
这个也会打印一些详细信息

package main
import "fmt"
import "errors"

func A()(int,error){
    panic("xxxd")
    return 1,errors.New("出错了")
}
func main () {
    if _,err:= A(); err!= nil{
        fmt.Println(err)
    }
}
//输出
panic: xxxd

goroutine 1 [running]:
main.A(...)
    /data/168122338030043882.go:6
main.main()
    /data/168122338030043882.go:10 +0x39
exit status 2

由于我们非主动调用panic,例如我们创建一个map没给初始化就去用它,也会包panic的错误。。这时候我们就用recover,这个函数能捕获到panic

package main
import "fmt"
import "errors"

func A()(int,error){

    defer func (){
        if r := recover() ;r != nil{
            fmt.Println("打印错误信息了",r)
        }
    }()
    var names map[string]string
    names["xx"] = "xx"
    return 1,errors.New("出错了")
}
func main () {
    if _,err:= A(); err!= nil{
        fmt.Println(err)
    }
}

//输出
打印错误信息了 assignment to entry in nil map

当然我们的 panic(“xxxd”),也能被recover捕获到
defer要放在panic前面,不然就捕获不到了。
recover只有在defer的函数种调用才会生效。
recover处理异常后逻辑并不会回复到panic的那个点去。会被直接return
多个defer会形成栈,后入先出

文档更新时间: 2023-04-11 22:47   作者:young