Golang基础知识三

函数

定义

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) (intint) {
 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:10

函数参数

函数参数有两种:

  • 值传递,实际调用函数时,参数会复制一份,实际操作的内容是赋值的内存,不会影响原值
  • 引用传递(地址传递),同样会复制一份地址值,所以实际操作会修改原值

经典例子交换两个数:

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(123))
 nums := []int{123}
 // 如果是切片传递方式,要加...
 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) intfunc(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{123}
 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.Newfmt.Errorf函数用于创建实现error 接口的错误对象,通过具体的错误对象类型来确认错误类型。类比Java的异常定义。

示例:

package main

import (
 "errors"
 "fmt"
)

var ErrDivideByZero = errors.New("除0错误")

func main() {
 defer func() {
  fmt.Println(recover())
 }()
 switch result, err := divide(100); 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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!