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