0.1、索引https://waterflow.link/articles/1666716727236
1、panic当我们执行panic的时候会结束下面的流程:
package mainimport "fmt"func main() { fmt.Println("hello") panic("stop") fmt.Println("world")}
go run 9.gohellopanic: stop
但是panic也是可以捕获的 , 我们可以使用defer和recover实现:
package mainimport "fmt"func main() { defer func() {if r := recover(); r != nil {fmt.Println("recover: ", r)} }() fmt.Println("hello") panic("stop") fmt.Println("world")}
go run 9.gohellorecover:stop
那什么时候适合panic呢?在 Go 中,panic 用于表示真正的异常,例如程序错误 。我们经常会在一些内置包里面看到panic的身影 。
比如strings.Repeat重复返回一个由字符串 s 的计数副本组成的新字符串:
func Repeat(s string, count int) string { if count == 0 {return "" } // if count < 0 {panic("strings: negative Repeat count") } else if len(s)*count/count != len(s) {panic("strings: Repeat count causes overflow") } ...}
我们可以看到当重复的次数小于0或者重复count次之后s的长度溢出,程序会直接panic,而不是返回错误 。这时因为strings包限制了error的使用,所以在程序错误时会直接panic 。
还有一个例子是关于正则表达式的例子:
package mainimport ( "fmt" "regexp")func main() { pattern := "a[a-z]b*" // 1 compile, err := regexp.Compile(pattern) // 2 if err != nil { // 2fmt.Println("compile err: ", err)return }// 3 allString := compile.FindAllString("acbcdadb", 3) fmt.Println(allString)}
- 编写一个正则表达式
- 调用Compile,解析正则表达式,如果成功 , 返回用于匹配文本的 Regexp 对象 。否则返回错误
- 利用正则,在输入的字符串中,获取所有的匹配字符
func MustCompile(str string) *Regexp { regexp, err := Compile(str) if err != nil {panic(`regexp: Compile(` + quote(str) + `): ` + err.Error()) } return regexp}
这个方法说明正则的解析是强依赖的,如果解析错误,直接panic结束程序 。用户可以根据实际情况选择 。但是实际开发中我们还是要谨慎使用panic,因为它会使程序结束运行(除非我们调用defer recover)
2、包装错误错误包装是将错误包装或者打包在一个包装容器中 , 这样的话我们就可以追溯到源错误 。错误包装的主要作用就是:
- 为错误添加上下文
- 将错误标记为特定类型的错误
package mainimport ( "fmt" "github.com/pkg/errors")type Courseware struct { Id int64 Code string Name string}func getCourseware(id int64) (*Courseware, error) { courseware, err := getFromDB(id) if err != nil {return nil, errors.Wrap(err, "六月的想访问这个课件") // 2 } return courseware, nil}func getFromDB(id int64) (*Courseware, error) { return nil, errors.New("permission denied") // 1}func main() { _, err := getCourseware(11) if err != nil {fmt.Println(err) }}
- 访问数据库时我们返回了原始的错误信息
- 到上层我们添加了一些自定义的上下文信息
go run 9.go六月的想访问这个课件: permission denied
当然我们也可以将错误包装成我们自定义类型的错误,我们稍微修改下上面的例子:package mainimport ( "fmt" "github.com/pkg/errors")type Courseware struct { Id int64 Code string Name string}// 1type ForbiddenError struct { Err error}// 2func (e *ForbiddenError) Error() string { return "Forbidden: " + e.Err.Error()}func getCourseware(id int64) (*Courseware, error) { courseware, err := getFromDB(id) if err != nil {return nil, &ForbiddenError{err} // 4 } return courseware, nil}func getFromDB(id int64) (*Courseware, error) { return nil, errors.New("permission denied") // 3}func main() { _, err := getCourseware(11) if err != nil {fmt.Println(err) }}
- 首先我们自定义了ForbiddenError的错误类型
- 我们实现了error接口
- 访问数据库抛出原始错误
- 上层返回ForbiddenError类型的错误
go run 9.goForbidden: permission denied
当然我们也可以不用创建自定义错误的类型,去包装错误添加上下文:package mainimport ( "fmt" "github.com/pkg/errors")type Courseware struct { Id int64 Code string Name string}func getCourseware(id int64) (*Courseware, error) { courseware, err := getFromDB(id) if err != nil {return nil, fmt.Errorf("another wrap err: %w", err) // 1 } return courseware, nil}func getFromDB(id int64) (*Courseware, error) { return nil, errors.New("permission denied")}func main() { _, err := getCourseware(11) if err != nil {fmt.Println(err) }}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 我的世界怎么制作下界通道地狱门(我的世界中的下界传送门怎么制作)
- SLAM中的内外点
- 高等数学符号的读法 的他符号
- flutter 系列之:flutter 中的幽灵offstage
- 为什么CSS中的calc函数可能会不生效?
- 俦杌这个字念什么(山海经中的梼杌)
- 原神3.0成就众花园中的一棵核桃树怎么完成
- nginx 客户端返回499的错误码
- JS中数值类型的本质
- 可处置的 ref 结构 C# 8.0 中的 Disposable ref structs