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教程。
