go函数
函数
package main
import (
"fmt"
//v2 "math/rand/v2"
//"weak"
)
//
func add(a,b int,)int{
//fmt.Println(c)
return a+b
}
//多个返回值
func add1(a,b int)(sun int,err error){
sun=a+b
return sun,err
}
//可变参数示例
func add2(a ...int)(sun int){
for _,v:=range a{
sun+=v
}
return
}
//前面这个string类型参数必须传,后面可变int类型参数可以不传
func add3(d string,c ...int)(sun int){
for _,v:=range c{
sun+=v
}
fmt.Println(d)
return
}
func runForever(){
for {
}
}
//函数返回函数
func cal(op string,i ...int) func(){
switch op{
case "+":
return func() {
fmt.Println("这是加法")
}
case "-":
return func() {
fmt.Println("这是减法")
}
case "*":
return func() {
fmt.Println("这是乘法")
}
case "/":
return func() {
fmt.Println("这是除法")
}
case "%":
return func() {
fmt.Println("这是取余")
}
default:
return func() {
fmt.Println("输入符号不正确")
}
}
}
//传递的参数也带有函数
func cal2(my func(i ...int) int ) int{
return my()
}
func main(){
q:=2
s:=7
t:=add(q,s)
fmt.Println(t)
//add函数赋值给a变量;注意不可带括号,带括号是调用
a:=add
//调用a变量函数返回值给c
c:=a(5,8)
fmt.Println(c)
//调用
cal2(func (i ...int) int{
sun := 0
for _,v :=range i{
sun+=v
}
return sun
})
}
Go 语言中的闭包(Closure),简单理解就是:一个函数可以“记住”并访问它定义时所在作用域中的变量,即使这些变量所在的函数已经执行结束了
示例
package main
import "fmt"
func add() func() int {
n := 0
return func() int {
n++
return n
}
}
func main() {
f := add()
fmt.Println(f()) // 1
fmt.Println(f()) // 2
fmt.Println(f()) // 3
}
执行结果
1
2
3
为什么能一直加
执行:
f := add()
时:
n := 0
本来应该随着 add() 结束而销毁。
但是返回的匿名函数:
func() int {
n++
return n
}
引用了 n。
于是 Go 发现:
这个变量后面还要用,不能销毁。
所以 n 会一直保存在内存中。
形成:
f
│
▼
匿名函数
│
▼
n = 0 → 1 → 2 → 3
这就是闭包。
示例2
func makeSuffix(suffix string) func(string) string { //返回值是一个带有string返回的函数
return func(name string) string {
return name + suffix
}
}
调用
func main() {
jpg := makeSuffix(".jpg")
fmt.Println(jpg("头像"))
fmt.Println(jpg("风景"))
}
#执行结果
头像.jpg
风景.jpg
这里:
suffix
被匿名函数记住了。
即使:
makeSuffix()
已经结束。
匿名函数仍然可以访问:
suffix
Go 闭包常见用途
1. 计数器
func counter() func() {
count := 0
return func() {
count++
fmt.Println(count)
}
}
c := counter()
c() //1
c() //2
c() //3
2. 状态保存例如记录用户访问次数:
func visit() func() int {
times := 0
return func() int {
times++
return times
}
}
延迟执行Go 经常配合:
defer
goroutine
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
这里就涉及闭包捕获变量的问题。
defer 关键字
defer 是 Go 中非常重要的关键字,它的作用是: 延迟执行某个函数,在当前函数返回之前执行。 理解 先登记 后执行
示例
package main
import "fmt"
func main() {
fmt.Println("1")
defer fmt.Println("2")
fmt.Println("3")
}
1
3
2
执行过程:
main开始
↓
打印1
↓
登记 defer fmt.Println("2")
(暂时不执行)
↓
打印3
↓
main 即将结束
↓
执行 defer
↓
打印2
多个 defer(后进先出)
func main() {
defer fmt.Println("A")
defer fmt.Println("B")
defer fmt.Println("C")
}
执行结果
C
B
A
压入:
A
B
C
弹出:
C
B
A
所以遵循:
LIFO(Last In First Out)
后进先出。
最常见用途——关闭资源
file, err := os.Open("a.txt")
if err != nil {
return
}
defer file.Close()
无论后面:
正常 return
出现错误
中途退出
都会执行:
file.Close()
避免资源泄露。
函数里defer 的使用
package main
import (
"fmt"
)
func d()(r int){
defer func () {
r ++
}()
return 10
}
func main(){
fmt.Println(d())
}
//执行结果
11
go 语言的错误处理机制
处理理念:开发函数时需要有一个返回值去告诉调用者是否成功,go设计者要求必须处理err,代码中大量会出现if err !=nil ;go设计者必须处理这个异常,防御编程
package main
import (
"errors"
"fmt"
)
func A() (int,error){
return 0, errors.New("失败")
}
func main(){
_,err:=A()
if err !=nil{
fmt.Println(err)
}
}
执行结果
失败
panic与recover
panic 函数会导致你的程序退出,不推荐;一般使用常景,一个服务启动必须依赖其他服务的时候,才使用panic;但是服务启动了,这个时候你某行代码种不小心执行panic那么不好意思你挂了。事故;架不住有些地方的代码写的不小心被动触发panic
recover 这个函数可以捕获到panic
1.defer 需要放在panic之前定义,另外recover 只有在defer调用的函数中才会生效
2.recover 处理后,逻辑并不会回复到panic 那个点去
3. 多个defer会形成栈,后定义的defer会先执行
草都可以从石头缝隙中长出来更可况你呢

浙公网安备 33010602011771号