开放的编程资料库

当前位置:我爱分享网 > Go教程 > 正文

如何在Golang中处理错误

Go错误教程展示了如何在Golang中处理错误。

错误

错误是程序中发生的异常的、意外的情况。在编程中,错误通常称为错误。发现和解决错误的过程称为调试。Go提供处理错误的工具。Goare纯值错误。因此,错误可以存储在变量中,作为参数传递给函数,或者从函数返回。

错误使用内置的error类型表示。

大多数Go函数在其返回值中返回一个错误值。(Go支持多个返回值。)我们有责任检查这个错误值。nil的值表示没有错误。按照惯例,错误值是返回值中最右边的值。

content, err := ioutil.ReadFile("thermopylae.txt")

if err != nil {
    log.Fatal(err)
}

处理错误的惯用方法是在函数调用后立即检查错误。

恐慌是我们不准备妥善处理的运行时错误。例如,当我们尝试除以零或尝试访问不存在的数组索引时,可能会发生恐慌。

堆栈跟踪是程序执行期间某个时间点的活动堆栈帧的报告。恐慌是Go将堆栈跟踪打印到控制台,协助我们进行调试。

$ go version
go version go1.18.1 linux/amd64

我们使用Go版本1.18。

去ioutil.ReadFile

ioutil.ReadFile读取文件并返回其内容。调用成功会将err设置为nil

The Battle of Thermopylae was fought between an alliance of Greek city-states,
led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the
course of three days, during the second Persian invasion of Greece.

这是要读取的文本文件。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
)

func main() {

    content, err := ioutil.ReadFile("thermopylae.txt")

     if err != nil {
          log.Fatal(err)
     }

    fmt.Println(string(content))
}

当我们读取文件时,很多事情都可能出错。例如,我们可能没有足够的权限来读取文件,我们提供了错误的文件路径,或者磁盘可能已满。

if err != nil {
    log.Fatal(err)
}

log.Fatal函数将错误打印到控制台并通过调用os.Exit终止程序。

$ go run read_file.go
The Battle of Thermopylae was fought between an alliance of Greek city-states,
led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the
course of three days, during the second Persian invasion of Greece.

当没有错误时,我们获取文件的内容。

$ go run read_file.go
2021/01/25 11:40:26 open thermopyla.txt: no such file or directory
exit status 1

错误的文件名会导致错误。

Go恐慌示例

恐慌是一个运行时错误。恐慌通常很难或不可能恢复。

package main

import "fmt"

func main() {

    var x int = 10
    var y int = 0

    fmt.Println(x / y)
}

零除法会导致运行时错误。

$ go run zero_div.go
panic: runtime error: integer divide by zero

goroutine 1 [running]:
main.main()
        /root/Documents/prog/golang/errors/zero_div.go:10 +0x11
exit status 2

此类错误必须由程序员修复。他必须通过始终检查分母来确保这种情况不会发生。

Go错误.New

可以使用errors.New函数创建新错误。它返回一个格式为给定文本的错误。即使文本相同,每次调用都会返回一个不同的错误值。

package main

import (
    "errors"
    "fmt"
)

func main() {

    err := errors.New("Some error")
    if err != nil {
        fmt.Println(err)
    }
}

该示例使用errors.New创建了一个新错误。

package main

import (
    "errors"
    "fmt"
    "log"
)

func divide(x int, y int) (int, error) {
    if y == 0 {
        return 0, errors.New("not possible to divide by zero")
    } else {
        return (x / y), nil
    }
}

func main() {

    if res, err := divide(6, 3); err != nil {
        log.Fatal(err)
    } else {
        fmt.Println("The answer is", res)
    }

    if res, err := divide(6, 0); err != nil {
        log.Fatal(err)
    } else {
        fmt.Println("The answer is", res)
    }
}

在这个例子中,我们确保分母不为零。

func divide(x int, y int) (int, error) {
    if y == 0 {
        return 0, errors.New("not possible to divide by zero")
    } else {
        return (x / y), nil
    }
}

如果分母为零,我们将使用errors.New生成一个新错误。

$ go run zero_div2.go
The answer is 2
2021/01/25 11:58:57 not possible to divide by zero
exit status 1

这一次,没有恐慌。我们已自行处理错误。

Goerrors.Is

errors.Is函数检查错误是否属于指定类型。

package main

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

func main() {
    if _, err := os.Open("data.txt"); err != nil {
        if errors.Is(err, os.ErrNotExist) {
            log.Fatal("file does not exist\t", err)
        } else if errors.Is(err, os.ErrPermission) {
            log.Fatal("insufficient permissions\t", err)

        } else {
            log.Fatal(err)
        }
    }

    fmt.Println("...")
}

在代码示例中,我们检查错误的类型是os.ErrNotExist还是os.ErrPermission

去fmt.Errorf

使用fmt.Errorf,我们可以创建一个带有格式化错误消息的新错误。

package main

import (
    "fmt"
    "math"
    "os"
)

func area(radius float64) (float64, error) {

    if radius < 0 {
        return 0, fmt.Errorf("radius %0.2f is less than zero", radius)
    }

    return math.Pi * radius * radius, nil
}

func main() {

    radius := -7.0
    area, err := area(radius)

    if err != nil {

        fmt.Println(err)
        os.Exit(1)
    }

    fmt.Printf("Area of circle %0.2f", area)
}

在代码示例中,我们计算圆的面积。还有一些错误处理,因为我们无法计算负半径的圆面积。

func area(radius float64) (float64, error) {

area函数返回两个值:计算出的面积和误差。

if radius < 0 {
    return 0, fmt.Errorf("radius %0.2f is less than zero", radius)
}

对于负的radius值,使用fmt.Errorf创建一个新的错误。

return math.Pi * radius * radius, nil

对于正确的radius值,我们返回计算出的面积并将错误设置为nil

area, err := area(radius)

我们调用area函数并获取返回值。

if err != nil {

    fmt.Println(err)
    os.Exit(1)
}

我们检查错误值;如果它不是nil,我们打印错误信息并退出程序。

$ go run circle_area.go
radius -7.00 is less than zero
exit status 1

Go自定义错误类型

为了定义自定义错误,我们实现了error接口。

package main

import (
    "fmt"
    "log"
)

func enterAge(age int) (string, error) {

    if age < 0 || age > 130 {

        return "", &wrongAge{age, "wrong age value"}
    }

    return fmt.Sprintf("processing %d age value", age), nil
}

type wrongAge struct {
    age int
    msg string
}

func (e *wrongAge) Error() string {

    return fmt.Sprintf("%d: %s", e.age, e.msg)
}

func main() {

    var age int = 18
    msg, err := enterAge(age)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(msg)

    age = 178
    msg, err = enterAge(age)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(msg)
}

在代码示例中,我们实现了一个自定义错误。

func enterAge(age int) (string, error) {

    if age < 0 || age > 130 {

        return "", &wrongAge{age, "wrong age value"}
    }

    return fmt.Sprintf("processing %d age value", age), nil
}

enterAge函数接受一个年龄值;对于超出预期年龄范围的值,会生成错误。

type wrongAge struct {
    age int
    msg string
}

wrongAge类型已定义。

func (e *wrongAge) Error() string {

    return fmt.Sprintf("%d: %s", e.age, e.msg)
}

实现了error接口。

$ go run custom_error.go 
processing 18 age value
2021/01/25 12:26:29 178: wrong age value
exit status 1

在本教程中,我们处理了Golang中的错误。

列出所有Go教程。

未经允许不得转载:我爱分享网 » 如何在Golang中处理错误

感觉很棒!可以赞赏支持我哟~

赞(0) 打赏