GO语言中的Defer与Error异常报错详细教程
目录标题
- 一、Defer
- 1. Example
- 2. Deferred methods 延迟方法
- 3. Arguments evaluation 延迟参数
- 4. Stack of defers 延迟程序堆栈
- 5. Practical use of defer 实际使用
- 二、Error
- 1. Example
- 2. PathError
- 3. DNSError
- 4. Direct Comparison 直接比较
- 5. Do not ignore errors 不要忽略错误
- 三、Custom Errors
- 1. Creating custom errors using the New function 创建自定义错误
- 2. Adding more information to the error using Errorf 添加更多信息
- 3. Providing more information about the error using struct type and fields 使用结构类型与字段展示错误
- 4. Providing more information about the error using methods on struct types 使用结构类型与方法展示错误
一、Defer
在Go语言中,defer 关键字用于延迟(defer)函数或方法的执行,即在函数执行完毕后再执行被延迟的代码块。defer 语句通常用于资源释放、
清理操作或确保某些代码在函数执行的最后一刻被执行。下面是对 defer 的几个关键点进行解释:
1.延迟执行: 使用 defer 可以将函数或方法的执行推迟到包含它的函数返回之前。当函数中的 defer 语句被执行时,相应的函数调用被推迟,并被压入一个栈中,直到包含函数返回时才会按照后进先出(LIFO)的顺序执行这些被推迟的函数调用。2.资源释放: defer 经常用于释放资源,比如打开的文件、数据库连接或网络连接等。通过在打开资源后使用 defer 来确保在函数返回之前关闭资源,可以避免资源泄漏,即使函数中途发生错误或提前返回,资源也会被正确释放。3.清理操作:defer 还可以用于执行一些清理操作,例如解锁互斥锁、关闭通道或恢复(recover)错误处理等。通过在函数开始时使用 defer,可以确保在函数执行结束时进行必要的清理。4.顺序执行: 多个 defer 语句按照它们的出现顺序进行执行。第一个 defer 语句的函数调用会在最后一个 defer 语句之前执行,即最后一个被推迟的函数调用会最先执行。
1. Example
package mainimport "fmt"func finished() {fmt.Println("Finished finding largest")}func largest(nums []int) {defer finished() // 延迟执行fmt.Println("Started finding largest")max := nums[0]for _, v := range nums {if v > max {max = v}}fmt.Println("Largest number in", nums, "is", max)}func main() {nums := []int{78, 111, 222, 333}largest(nums)}// Started finding largest// Largest number in [78 111 222 333] is 333// Finished finding largest
2. Deferred methods 延迟方法
type Person struct {firstName stringlastName string}func (p Person) fullName() {fmt.Printf("%s %s", p.firstName, p.lastName)}func main() {p := Person{firstName: "Mei",lastName: "Jin",}defer p.fullName()fmt.Printf("Welcome ")}// Welcome Mei Jin
3. Arguments evaluation 延迟参数
func printA(a int) {fmt.Println("Value of a in deferred function", a)}func main() {a := 5defer printA(a)a = 10fmt.Println("Values of before deferred function call", a)}// Values of before deferred function call 10// Value of a in deferred function 5
4. Stack of defers 延迟程序堆栈
func main() {name := "Like"fmt.Printf("Original String: %s\n", string(name))fmt.Printf("Reversed String: ")for _, v := range name {defer fmt.Printf("%c", v) // defer表示延迟调用 但是堆栈的数据时后进先出 所以导致字符串反转}}// Original String: Like// Reversed String: ekiL
5. Practical use of defer 实际使用
package mainimport ( "fmt""sync")type rect struct { length intwidth int}func (r rect) area(wg *sync.WaitGroup) { defer wg.Done()if r.length < 0 {fmt.Printf("rect %v's length should be greater than zero\n", r)return}if r.width < 0 {fmt.Printf("rect %v's width should be greater than zero\n", r)return}area := r.length * r.widthfmt.Printf("rect %v's area %d\n", r, area)}func main() { var wg sync.WaitGroupr1 := rect{-67, 89}r2 := rect{5, -67}r3 := rect{8, 9}rects := []rect{r1, r2, r3}for _, v := range rects {wg.Add(1)go v.area(&wg)}wg.Wait()fmt.Println("All go routines finished executing")}// rect {8 9}'s area 72 // rect {-67 89}'s length should be greater than zero // rect {5 -67}'s width should be greater than zero // All go routines finished executing
二、Error
1. Example
package mainimport ("fmt""os")func main() {f, err := os.Open("/test.txt") // 没有该文件if err != nil {fmt.Println(err)return}fmt.Println(f.Name(), "Opened successfully")}// open test.txt: The system cannot find the file specified.
2. PathError
package mainimport ("errors""fmt""os")func main() {f, err := os.Open("/test.txt")if err != nil {var pErr *os.PathErrorif errors.As(err, &pErr) {fmt.Println("Failed to open file at path", pErr.Path)return}fmt.Println("Generic error", err)return}fmt.Println(f.Name(), "Opened successfully")}// Failed to open file at path /test.txt
3. DNSError
package mainimport ("errors""fmt""net")func main() {addr, err := net.LookupHost("www.baidu.com")//addr, err := net.LookupHost("golangbot123.com")if err != nil {var dnsErr *net.DNSErrorif errors.As(err, &dnsErr) {if dnsErr.Timeout() { // 超时错误fmt.Println("operation time out")return}if dnsErr.Temporary() { // 临时错误fmt.Println("Temporary error")return}fmt.Println("Generic DNS error", err) // 没有这个站点return}fmt.Println("Generic error", err)return}fmt.Println(addr) // [180.101.50.242 180.101.50.188]}// 没有站点报错 Generic DNS error lookup golangbot123.com: no such host// 有站点 [180.101.50.242 180.101.50.188]
4. Direct Comparison 直接比较
package mainimport ("errors""fmt""path/filepath")func main() {files, err := filepath.Glob("go.mod")if err != nil {if errors.Is(err, filepath.ErrBadPattern) {fmt.Println("Bad pattern error:", err) // syntax error in patternreturn}fmt.Println("Generic error:", err) // 类型错误return}fmt.Println("matched files", files) // 正确打印列表 matched files [go.mod]}
5. Do not ignore errors 不要忽略错误
package mainimport ( "fmt""path/filepath")func main() { files, _ := filepath.Glob("[") // 匹配错误 如果不展示则找起错误就麻烦了fmt.Println("matched files", files)}// matched files []
三、Custom Errors
1. Creating custom errors using the New function 创建自定义错误
package mainimport ("errors""fmt""math")func circleArea(radius float64) (float64, error) {if radius < 0 { // 如果小于0则运行以下return 0, errors.New("Area calculation failed, radius is less than zero")}return math.Pi * radius * radius, nil}func main() {radius := -10.00area, err := circleArea(radius)if err != nil { // 如果为空fmt.Println(err)return}fmt.Printf("Area of circle %0.2f", area)}// Area calculation failed, radius is less than zero
2. Adding more information to the error using Errorf 添加更多信息
package mainimport ("fmt""math")func circleArea(radius float64) (float64, error) {if radius < 0 { // 如果小于0则运行以下return 0, fmt.Errorf("Area calculation failed, radius %0.2f is less than zero", radius)}return math.Pi * radius * radius, nil}func main() {radius := -10.00area, err := circleArea(radius)if err != nil { // 如果为空fmt.Println(err)return}fmt.Printf("Area of circle %0.2f", area)}// Area calculation failed, radius -10.00 is less than zero
3. Providing more information about the error using struct type and fields 使用结构类型与字段展示错误
package mainimport ("errors""fmt""math")type areaError struct {err stringradius float64}func (e *areaError) Error() string {return fmt.Sprintf("radius %0.2f:%s", e.radius, e.err)}func circleArea(radius float64) (float64, error) {if radius < 0 { // 如果小于0则运行以下return 0, &areaError{err: "radius is negative",radius: radius,}}return math.Pi * radius * radius, nil}func main() {radius := -20.0area, err := circleArea(radius)if err != nil {var areaError *areaErrorif errors.As(err, &areaError) {fmt.Printf("Area calculation failed, radius %0.2f is less than zero", areaError.radius)return}fmt.Println(err)return}fmt.Printf("Area of rectangle %0.2f", area)}// Area calculation failed, radius -20.00 is less than zero
4. Providing more information about the error using methods on struct types 使用结构类型与方法展示错误
package mainimport ( "errors""fmt")type areaError struct { err string //error descriptionlength float64 //length which caused the errorwidth float64 //width which caused the error}func (e *areaError) Error() string { return e.err}func (e *areaError) lengthNegative() bool { return e.length < 0}func (e *areaError) widthNegative() bool { return e.width < 0}func rectArea(length, width float64) (float64, error) { err := ""if length < 0 {err += "length is less than zero"}if width < 0 {if err == "" {err = "width is less than zero"} else {err += ", width is less than zero"}}if err != "" {return 0, &areaError{err: err,length: length,width: width,}}return length * width, nil}func main() { length, width := -5.0, -9.0area, err := rectArea(length, width)if err != nil {var areaError *areaErrorif errors.As(err, &areaError) {if areaError.lengthNegative() {fmt.Printf("error: length %0.2f is less than zero\n", areaError.length)}if areaError.widthNegative() {fmt.Printf("error: width %0.2f is less than zero\n", areaError.width)}return}fmt.Println(err)return}fmt.Println("area of rect", area)}// error: length -5.00 is less than zero// error: width -9.00 is less than zero
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请点点赞收藏+关注谢谢支持 !!!