这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天
Go18
go 1.18 的正式版(go version go1.18 windows/amd64 ),今天我们就GO的新特性:泛型 进行简单的尝鲜使用。 GO 中泛型涉及到两个关键词:类型参数、类型约束
值、引用、指针
泛型和约束
GO 中泛型的语法
以下示例中 [] 类型参数type0、type2,其中type0受可比较类型约束 ,type1 受 int64 或 float64 类型约束。
func funcName[type0 comparable, type1 int64 | float64](arg0 type0, arg1 type1) {}
该函数内部打印每个参数的类型和值
func main() {
funcName("arg0", 1)
funcName("arg0", 3.5)
}
func funcName[type0 comparable, type1 int | float64](arg0 type0, arg1 type1) {
fmt.Printf("arg0 type: %T value: %v\t", arg0, arg0)
fmt.Printf("arg1 type: %T value: %v\n", arg1, arg1)
}
结果:
arg0 type: string value: arg0 arg1 type: int value: 1
arg0 type: string value: arg0 arg1 type: float64 value: 3.5
Go泛型 若存在违反泛型函数中的类型约束,能够在编译时捕获
我们尝试给funcName 函数的第二个参数传如string字面量
func main(){
funcName("arg0", "string")
}
编译结果:
command-line-arguments
.\test_1.go:9:10: string does not implement int|float64
指定类型参数调用
指定类型参数-在方括号内的类型名称-来明确你所调用的函数中应该用哪些类型来替代类型参数
func main(){
funcName[string, int]("arg0", 4)
}
通过interfac进行类型约束
类型约束可以通过interface 进行绑定
声明一个Number interface类型作为类型限制 在interface内声明int64和float64的合集
type Number interface {
float64 | int
修改实例函数 arg1 类型参数的类型约束为接口 Number
func main() {
funcName("arg0", 1)
funcName("arg0", 3.5)
}
func funcName[type0 comparable, type1 Number](arg0 type0, arg1 type1) {
fmt.Printf("arg0 type: %T value: %v\t", arg0, arg0)
fmt.Printf("arg1 type: %T value: %v\n", arg1, arg1)
}
执行结果
arg0 type: string value: arg0 arg1 type: int value: 1
arg0 type: string value: arg0 arg1 type: float64 value: 3.5
# Go语言退出、结束函数或者协程方式
::: tip Go语言中有很多让我觉得误解的一些终结语句,我想 block 和 continue 只是最基本的也是最简单的。
Go语言几种退出程序的方式:
os.Exit()退出程序panic()抛出异常return退出函数defer + recover()捕获异常runtime.Goexit()退出当前协程os.Kill杀死进程os.Interrupt中断进程block跳出循环 (不讲)continue跳出当前循环 继续下一次循环 (不讲)
:::
runtime.Goexit() 退出当前协程
💡简单的一个案例如下:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
//Go语言几种退出程序的方式
//1. os.Exit() 退出程序
//2. panic() 抛出异常
//3. return 退出函数
//4. defer + recover() 捕获异常
//5. runtime.Goexit() 退出当前协程
//6. os.Kill 杀死进程
//7. os.Interrupt 中断进程
//8. block 跳出循环
//9. continue 跳出当前循环 继续下一次循环
fmt.Println("main() 开始的部分")
go func() {
fmt.Println("go func() 开始的部分")
func() {
fmt.Println("func() 开始的部分")
go func() {
fmt.Println("最里面go func() 开始的部分")
runtime.Goexit() //退出当前协程
fmt.Println("最里面go func() 结束的部分")
}()
fmt.Println("func() 结束的部分")
//等待2s
time.Sleep(2 * time.Second)
}()
fmt.Println("go func() 结束的部分")
}()
//等待5s
time.Sleep(time.Second * 5)
fmt.Println("main() 结束的部分")
}
🚀 编译结果如下:
[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\61-main.go"
main() 开始的部分
go func() 开始的部分
func() 开始的部分
最里面go func() 开始的部分
func() 结束的部分
go func() 结束的部分
main() 结束的部分
::: warning 📜 对上面的解释:
runtime.Goexit() //退出当前协程
只是退出当前的 go func() 协程,并不是当前函数。
:::
os.Exit(-1) 退出当前程序
::: tip 函数定义: 函数定义:func Exit(code int)
Exit 函数可以让当前程序以给出的状态码 code 退出。一般来说,状态码 0 表示成功,非 0 表示出错。程序会立刻终止,并且 defer 的函数不会被执行。
package main
import (
"fmt"
"os"
)
func main() {
var num int = 0
fmt.Printf("Enter number:")
fmt.Scanf("%d", &num)
if num > 0 {
fmt.Printf("n > 0 Program terminated\n")
os.Exit(0)
} else if num < 0 {
fmt.Printf("n < 0 Program continue\n")
}
fmt.Printf("Program finished normally\n")
}
🚀 编译结果如下:
PS D:\文档\最近的\awesome-golang\docs\code\go-super> go run .\66-main.go
Enter number:2
n > 0 Program terminated
PS D:\文档\最近的\awesome-golang\docs\code\go-super> go run .\66-main.go
Enter number:-1
n < 0 Program continue
Program finished normally
PS D:\文档\最近的\awesome-golang\docs\code\go-super> go run .\66-main.go
Enter number:0
Program finished normally
:::
💡简单的一个案例如下:
package main
import (
"fmt"
"os"
"time"
)
func main() {
fmt.Println("main() 开始的部分")
go func() {
fmt.Println("go func() 开始的部分")
func() {
fmt.Println("func() 开始的部分")
go func() {
fmt.Println("最里面go func() 开始的部分")
os.Exit(-1) //退出程序
fmt.Println("最里面go func() 结束的部分")
}()
//等待2s
time.Sleep(2 * time.Second)
fmt.Println("func() 结束的部分")
}()
fmt.Println("go func() 结束的部分")
}()
//等待5s
time.Sleep(time.Second * 5)
fmt.Println("main() 结束的部分")
}
🚀 编译结果如下:
[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\61-main.go"
main() 开始的部分
go func() 开始的部分
func() 开始的部分
最里面go func() 开始的部分
exit status 0xffffffff
::: warning 📜 对上面的解释:
os.Exit(-1) //退出程序
退出当前的 func main() 程序
:::
os.Exit(0) 中断进程
💡简单的一个案例如下:
package main
import (
"fmt"
"os"
"time"
)
func main() {
fmt.Println("main() 开始的部分")
go func() {
fmt.Println("go func() 开始的部分")
func() {
fmt.Println("func() 开始的部分")
go func() {
fmt.Println("最里面go func() 开始的部分")
//中断进程
os.Exit(0)
fmt.Println("最里面go func() 结束的部分")
}()
//等待2s
time.Sleep(2 * time.Second)
fmt.Println("func() 结束的部分")
}()
fmt.Println("go func() 结束的部分")
}()
//等待5s
time.Sleep(time.Second * 5)
fmt.Println("main() 结束的部分")
}
🚀 编译结果如下:
[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\63-main.go"
main() 开始的部分
go func() 开始的部分
func() 开始的部分
最里面go func() 开始的部分
::: warning 📜 对上面的解释: 状态码为 0 ,退出但是不报错
:::
panic() 抛出异常退出
::: details 或许你可以使用defer func() 抛出异常继续执行
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("main() 开始的部分")
go func() {
fmt.Println("go func() 开始的部分")
func() {
fmt.Println("func() 开始的部分")
go func() {
fmt.Println("最里面go func() 开始的部分")
//defer异常捕获
defer func() {
if err := recover(); err != nil {
fmt.Println("最里面go func() defer异常捕获", err)
}
}()
panic("最里面go func() 抛出异常退出")
fmt.Println("最里面go func() 结束的部分")
}()
//等待2s
time.Sleep(2 * time.Second)
fmt.Println("func() 结束的部分")
}()
fmt.Println("go func() 结束的部分")
}()
//等待5s
time.Sleep(time.Second * 5)
fmt.Println("main() 结束的部分")
}
🚀 编译结果如下:
[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\61-main.go"
main() 开始的部分
go func() 开始的部分
func() 开始的部分
最里面go func() 开始的部分
最里面go func() defer异常捕获 最里面go func() 抛出异常退出
func() 结束的部分
go func() 结束的部分
main() 结束的部分
:::
💡简单的一个案例如下:
package main
import (
"fmt"
"time"
)
func main() {
//Go语言几种退出程序的方式
//1. os.Exit() 退出程序
//2. panic() 抛出异常
//3. return 退出函数
//4. defer + recover() 捕获异常
//5. runtime.Goexit() 退出当前协程
//6. os.Kill 杀死进程
//7. os.Interrupt 中断进程
//8. block 跳出循环
//9. continue 跳出当前循环 继续下一次循环
fmt.Println("main() 开始的部分")
go func() {
fmt.Println("go func() 开始的部分")
func() {
fmt.Println("func() 开始的部分")
go func() {
fmt.Println("最里面go func() 开始的部分")
panic("最里面go func() 抛出异常退出")
fmt.Println("最里面go func() 结束的部分")
}()
//等待2s
time.Sleep(2 * time.Second)
fmt.Println("func() 结束的部分")
}()
fmt.Println("go func() 结束的部分")
}()
//等待5s
time.Sleep(time.Second * 5)
fmt.Println("main() 结束的部分")
}
🚀 编译结果如下:
[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\61-main.go"
main() 开始的部分
go func() 开始的部分
func() 开始的部分
最里面go func() 开始的部分
panic: 最里面go func() 抛出异常退出
goroutine 7 [running]:
main.main.func1.1.1()
d:/文档/最近的/awesome-golang/docs/code/go-super/61-main.go:28 +0x65
created by main.main.func1.1
d:/文档/最近的/awesome-golang/docs/code/go-super/61-main.go:25 +0x65
exit status 2
::: warning 📜 对上面的解释: 可以看出来这两者之间的差距,recover()使得 程序结束当前协程~继续执行使用。
不使用的化,会直接退出~
:::
return 跳出当前函数
return 是我们最常用的一个用法:在设定返回值为1002,然后return触发defer语句,执行完成后(即便是defer抛出panic后被捕获),返回1002。
我们都知道 panic 会触发defer: 由于 return会触发 defer,函数抛出 panic 也会触发 defer。所以我们可以在 defer 中,特别是通过 recovery() 函数捕获 panic 后,修改函数的返回值。
func main() {
fmt.Println(Test0())
}
func Test0()(ret string){
defer func() {
err:=recover()
if err!=nil{
ret=fmt.Sprint(err)
}
}()
panic("this is a panic")
return "normal"
}
🚀 编译结果如下:
this is a panic
::: warning 📜 对上面的解释: 注意 panic 和 return 的循序很重要,如果当 return "normal" 在前面,那么很明显会输出 normal ,this is a panic 将不会输出。
:::
💡简单的一个案例如下:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("main() 开始的部分")
go func() {
fmt.Println("go func() 开始的部分")
func() {
fmt.Println("func() 开始的部分")
go func() {
fmt.Println("最里面go func() 开始的部分")
return
fmt.Println("最里面go func() 结束的部分")
}()
//等待2s
time.Sleep(2 * time.Second)
fmt.Println("func() 结束的部分")
}()
fmt.Println("go func() 结束的部分")
}()
//等待5s
time.Sleep(time.Second * 5)
fmt.Println("main() 结束的部分")
}
🚀 编译结果如下:
[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\61-main.go"
main() 开始的部分
go func() 开始的部分
func() 开始的部分
最里面go func() 开始的部分
func() 结束的部分
go func() 结束的部分
main() 结束的部分
::: warning📜 对上面的解释: return 直接跳出当前的函数, 注意这个函数也可以是当前的协程 go func()
:::
Lable跳转标签
💡简单的一个案例如下:
package main
import (
"fmt"
"time"
)
func main() {
//Go语言几种退出程序的方式
//1. os.Exit() 退出程序
//2. panic() 抛出异常
//3. return 退出函数
//4. defer + recover() 捕获异常
//5. runtime.Goexit() 退出当前协程
//6. os.Kill 杀死进程
//7. os.Interrupt 中断进程
//8. block 跳出循环
//9. continue 跳出当前循环 继续下一次循环
fmt.Println("main() 开始的部分")
go func() {
fmt.Println("go func() 开始的部分")
func() {
fmt.Println("func() 开始的部分")
go func() {
Lable1:
fmt.Println("最里面go func() 开始的部分")
//等待1s
time.Sleep(time.Second)
// 标签跳转
goto Lable1
fmt.Println("最里面go func() 结束的部分")
}()
//等待2s
time.Sleep(2 * time.Second)
fmt.Println("func() 结束的部分")
}()
fmt.Println("go func() 结束的部分")
}()
//等待5s
time.Sleep(time.Second * 5)
fmt.Println("main() 结束的部分")
}
🚀 编译结果如下:
PS D:\文档\最近的\awesome-golang\docs\code\go-super> go run .\64-main.go
main() 开始的部分
go func() 开始的部分
func() 开始的部分
最里面go func() 开始的部分
最里面go func() 开始的部分
func() 结束的部分
go func() 结束的部分
最里面go func() 开始的部分
最里面go func() 开始的部分
最里面go func() 开始的部分
main() 结束的部分