一、错误
go语言哲学:面向错误的编程,你只要发现了错误就得解决它,而不对错误以外的值进行期待。在go语言中,用error接口表示错误,任何实现此接口的类型都当成一个错误
type error interface{
Error ()stirng
}
1. 返回错误
errors.New
package main
import (
"errors"
"fmt")
// 定义User结构体,包含Name字段
type User struct {
Name string
}
// 生成User对象的函数,返回*User和error
func genUser() (*User, error) {
// 返回nil和自定义错误
return nil, errors.New("user == nil")
}
func main() {
// 调用genUser并同时接收返回的用户对象和错误
if u, err := genUser(); err != nil {
// 若有错误,打印错误信息
fmt.Println(err)
} else {
// 若无错误,打印用户姓名
fmt.Println(u.Name)
}
}
代码先定义User结构体和生成用户的genUser函数(该函数固定返回错误),在main函数中调用genUser,通过if-else结构先判断是否有错误,有错误则打印错误,无错误则打印用户姓名,体现了 Go 语言 “面向错误编程” 的风格,优先处理错误,再处理正常逻辑
fmt.Errorf
package main
import (
"fmt"
"errors"
)
func main() {
money := 1
err := pay(money)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("支付成功")
}
}
func pay(money int) error {
if money < 10 {
return fmt.Errorf("余额不足")
}
return nil
}
2. 判断特定错误
errors.Is
func main() {
// 定义一个自定义错误
var customErr = errors.New("自定义错误")
// 模拟函数返回错误
err := doSomething()
if err != nil {
//使用 errors.Is 判断错误是否为 customErr
if errors.Is(err, customErr) {
fmt.Println("捕获到自定义错误")
} else {
fmt.Println("未知错误:", err)
}
}
}
func doSomething() error {
// 这里返回我们定义的自定义错误
return errors.New("函数错误")
}
自定义错误为customErr,写了一个函数返回错误为“函数错误”,errors.Is来判断这两个错误是不是一样
3. 封装与合并
两次都会打印 这两个错误相等,因为封装和解包后,原始错误的 “身份” 始终保持一致:
func main() {
originalErr := errors.New("原始错误")
// 用 %w 封装原始错误,生成新错误(保留原始错误的“身份”)
newError := fmt.Errorf("error: %w", originalErr)
// 用 errors.Is 判断“封装后的错误”是否等价于“原始错误”
if errors.Is(newError, originalErr) {
fmt.Println("这两个错误相等")
}
// 用 errors.Unwrap 解包,获取原始错误
originalErr1 := errors.Unwrap(newError)
// 再次判断解包后的错误是否和原始错误等价
if errors.Is(originalErr, originalErr1) {
fmt.Println("这两个错误相等")
}
}
会依次打印 err is err1 和 err is err2,因为合并后的错误包含了这两个子错误:
func main() {
err1 := errors.New("err1")
err2 := errors.New("err2")
// 合并多个错误为一个错误
err := errors.Join(err1, err2)
// 判断合并后的错误是否包含 err1
if errors.Is(err, err1) {
fmt.Println("err is err1")
}
// 判断合并后的错误是否包含 err2
if errors.Is(err, err2) {
fmt.Println("err is err2")
}
}
4. 程序崩溃:panic
panic比较特殊,属于中断程序。使用场景:当程序不能恢复时,但是程序的崩溃是不可接受的,我们需要去预料它
捕获
捕获panic前,执行defer延迟操作,使用recover执行panic指令
二、延迟调用
关键字defer。defer后面跟的函数叫延迟函数,一般在return前或者在panic前才执行
后进先出
func main() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
}