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 main

        import "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 string
            lastName  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 := 5
            defer printA(a)
            a = 10
            fmt.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 main

        import (  
            "fmt"
            "sync"
        )

        type rect struct {  
            length int
            width  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.width
            fmt.Printf("rect %v's area %d\n", r, area)
        }

        func main() {  
            var wg sync.WaitGroup
            r1 := 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 main

        import (
            "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 main

        import (
            "errors"
            "fmt"
            "os"
        )

        func main() {
            f, err := os.Open("/test.txt")
            if err != nil {
                var pErr *os.PathError
                if 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 main

        import (
            "errors"
            "fmt"
            "net"
        )

        func main() {
            addr, err := net.LookupHost("www.baidu.com")
            //addr, err := net.LookupHost("golangbot123.com")
            if err != nil {
                var dnsErr *net.DNSError
                if 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 main

        import (
            "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 pattern
                    return
                }
                fmt.Println("Generic error:", err) // 类型错误
                return
            }
            fmt.Println("matched files", files) // 正确打印列表	matched files [go.mod]
        }

5. Do not ignore errors 不要忽略错误

        package main

        import (  
            "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 main

        import (
            "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.00
            area, 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 main

        import (
            "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.00
            area, 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 main

        import (
            "errors"
            "fmt"
            "math"
        )

        type areaError struct {
            err    string
            radius 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.0
            area, err := circleArea(radius)
            if err != nil {
                var areaError *areaError
                if 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 main

        import (  
            "errors"
            "fmt"
        )

        type areaError struct {  
            err    string  //error description
            length float64 //length which caused the error
            width  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.0
            area, err := rectArea(length, width)
            if err != nil {
                var areaError *areaError
                if 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

技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请点点赞收藏+关注谢谢支持 !!!

你可能感兴趣的:(Go,golang,开发语言,后端,go)