Go 中的错误


2022年1月21日, Learn eTutorial
2180

在本教程中,您将学习如何使用错误以及如何创建自己的自定义错误。Go 使用类似于 C 语言的错误处理方法,没有任何异常,但在其他语言(如 JAVA)中使用 try/catch 异常。Go 错误处理很简单,具有预定义的错误类型。
此外,我们还将在后面的教程中学习如何使用 panic 生成panic 错误和使用recover来克服它们。

Golang中的错误是什么?

错误是在正在执行的 Go 程序中发生的一个意外操作或事件。程序中的错误会影响正常的执行流程,并导致程序突然终止。错误也称为 bug。用于查找和解决编程语言中 bug 的方法称为调试。

Go 允许包和工具处理错误(即失误)。为了实现操作错误的功能,Go 提供了错误包。Go 对函数和方法中的微小错误返回一个错误对象。错误对象可能是唯一或最后一个返回值。如果函数没有问题,错误对象为 nil。如果我们收到调用语句中的任何错误,我们应该始终验证它们。

Go 编程语言不支持异常处理机制,与许多其他语言不同,Golang 不能抛出异常。相反,Golang 采用了新的错误处理机制,如 panic 和 recovery。

如何在Golang中声明错误?

错误是一种类型。错误具有一个 Error() 方法,此方法以字符串形式返回错误消息的详细信息。错误是一种类型,其


// The built-in error interface is a regular interface for handling errors. 
// where nil means no errors. 
type error interface { 
     Error() string 
     }

如您所见,错误处理程序实际上是一个接口,其中包含一个简单的 Error() 方法,其返回值为字符串。

此定义告诉我们,要实现错误处理,所需做的就是向 Error() 方法返回一个简单的字符串。


func main() {
    conent, err := openFile()
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(string(conent))
    }
}

如果 err != nil(检测到错误存在),它将终止执行,否则将继续正常执行。

在 Go 中,许多函数返回多个值。通常,其中一个返回值是 error 类型。一个例子是 strconv.Atoi() 函数。此函数用于将字符串数据转换为数字。此函数返回 2 个返回值。第一个返回值是转换结果,第二个返回值是错误。当转换顺利时,第二个返回值将为 nil。同时,当转换失败时,可以立即从返回的错误中识别出原因。

下面是一个简单程序示例,用于检测用户输入是数字还是非数字。从中我们将学习错误的使用。


package main
import (
    "fmt"
    "strconv"
)

func main() {
    var input string
    fmt.Print("Type some number: ")
    fmt.Scanln(&input)

    var number int
    var err error
    number, err = strconv.Atoi(input)

    if err == nil {
        fmt.Println(number, "is number")
    } else {
        fmt.Println(input, "is not number")
        fmt.Println(err.Error())
    }
}

运行程序,它会显示“输入一些数字:”。输入一个自由数字,如果是,则按回车键。

输出


Type some number:  is not number
strconv.Atoi: parsing "": invalid syntax

fmt.Scanln(&input) 语句用于捕获用户在按下回车键之前输入的文本,然后将其作为字符串保存到 input 变量中。

然后使用 strconv.Atoi() 将变量转换为数字类型。该函数返回 2 个数据,由 number 和 err 接收。

第一个数据 (number) 包含转换结果。第二个数据 err 包含错误信息(如果在转换过程中发生错误)。

之后进行检查,如果没有错误,则显示数字。如果有错误,则显示输入以及错误消息。错误消息可以从错误类型的 Error() 方法中获取。

如何创建自定义错误?

除了利用可用内部函数返回的错误之外,我们还可以使用 errors.New() 函数创建自己的错误对象(必须先导入 errors 包)。

以下示例展示了如何创建自定义错误。首先,准备一个名为 validate() 的函数,该函数将用于检查输入是否为空。如果为空,将生成一个新错误。


Package main

import (
    "errors"
    "fmt"
    "strings"
)

func validate(input string) (bool, error) {
    if strings.TrimSpace(input) == "" {
        return false, errors.New("cannot be empty")
    }
    return true, nil
}

接下来在 main 函数中,创建一个简单的过程来捕获用户输入。利用 validate() 函数来检查输入。

在上面的程序中,函数 error.New() 定义了一个来自 error 包的新错误类型,并在括号内传递了相应的错误消息。


func main() {
    var name string
    fmt.Print("Type your name: ")
    fmt.Scanln(&name)

    if valid, err := validate(name); valid {
        fmt.Println("hello", name)
    } else {
        fmt.Println(err.Error())
    }
}

validate() 函数返回 2 条记录。第一个数据是一个布尔值,指示输入是否有效。第二个数据是错误消息(如果输入无效)。

strings.TrimSpace() 函数用于删除字符串前后空格字符。这是必需的,因为用户可以只输入一个空格然后回车。

当输入无效时,使用 errors.New() 函数创建一个新错误。此外,还可以通过 fmt.Errorf() 函数创建错误对象。

自定义错误程序


package main

import (
    "errors"
    "fmt"
    "strings"
)

func validate(input string) (bool, error) {
    if strings.TrimSpace(input) == "" {
        return false, errors.New("cannot be empty")
    }
    return true, nil
}
func main() {
    var name string
    fmt.Print("Type your name: \n")
    fmt.Scanln(&name)

    if valid, err := validate(name); valid {
        fmt.Println("hello", name)
    } else {
        fmt.Println(err.Error())
    }

输出


Type your name: 
cannot be empty

Golang中的ioutil.ReadFile

在 Go 编程语言中,ioutil.ReadFile 读取文件并返回其内容。如果执行成功,调用会将 err 设置为 nil。
考虑一个带有 test.txt 文件的读取文件,其文本如下所示:

GO : Error

通过 Go 代码读取文件 test.txt 的程序如下


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

var path = "C:/Users/Desktop/Golang/Tutorials/test.txt"

func main() {

 content, err := ioutil.ReadFile(path)

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

 fmt.Println(string(content))
}  }
 }
 if err != nil {
  panic(err)
 }

 fmt.Println("==> file successfull")
 fmt.Println(string(text))
}

输出


Windows PowerShell
Copyright (C) 2014 Microsoft Corporation. All rights reserved.

PS C:\Users\Desktop\Golang> go run errorread.go
hello welcome to golang

通过设置错误的路径会发生错误,这会提供错误指示。为了识别差异,我们使用相同的程序,但将路径中的测试文件名更改为文本文件。


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

var path = "C:/Users/Desktop/Golang/Tutorials/text.txt"

func main() {

 content, err := ioutil.ReadFile(path)

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

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


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

输出


PS C:\Users\Desktop\Golang> go run errorread.go
2022/01/08 12:38:49 open C:/Users/Desktop/Golang/Tutorials/text.txt: The system cannot find the file specified.
exit status 1

注意:要理解读取文件概念,请参阅文件教程

Golang errors.Is

errors.Is 函数检查错误是否为指定类型。
让我们通过一个示例来理解它。


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

var path = "C:/Users/Desktop/Golang/Tutorials/test.txt"

func main() {
 if _, err := os.Open(path); 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 类型。

输出


Windows PowerShell
Copyright (C) 2014 Microsoft Corporation. All rights reserved.

PS C:\Users\Desktop\Golang> go run isfun.go
...

error包的Is()函数的工作原理

让我们通过一个程序来理解


package main
import (
 "errors"
 "fmt"
)
 const Wr

var ErrInput = errors.New("bad input")

func validateInput(input string) error {
 if input == WrongInput {
  return fmt.Errorf("validateInput: %w", ErrInput)
 }
 return nil
}

func main() {
 input := WrongInput

 err := validateInput(input)
 if errors.Is(err, ErrInput) {
  fmt.Println("error due to wrong input")
 }
}

在上述程序中,您可以观察到函数 validate inputWrongInput 返回一个错误。此错误是 ErrInput,它包装在由 fmt.Errorf() 创建的错误中。使用 Is(err, target error) bool 函数,即使错误被包装,我们也可以检测到 ErrInput,因为此函数会检查包装错误链中是否有任何错误与目标匹配。因此,这种形式应该优于 err == ErrInput 的比较。

输出


error due to wrong input

Program exited.