go入门教程- 13.1 错误处理

  1. 13.1 错误处理
    1. 13.1.1 定义错误
    2. 13.1.2 用 fmt 创建错误对象
    3. 链接

13.1 错误处理

Go 有一个预先定义的 error 接口类型

1
2
3
type error interface {
Error() string
}

错误值用来表示异常状态;我们可以在 5.2 节中看到它的标准用法。处理文件操作的例子可以在 12 章找到;我们将在 15 章看到网络操作的例子。errors 包中有一个 errorString 结构体实现了 error 接口。当程序处于错误状态时可以用 os.Exit(1) 来中止运行。

13.1.1 定义错误

任何时候当你需要一个新的错误类型,都可以用 errors(必须先 import)包的 errors.New 函数接收合适的错误信息来创建,像下面这样:

1
err := errors.New("math - square root of negative number")

在示例 13.1 中你可以看到一个简单的用例:

示例 13.1 errors.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// errors.go
package main

import (
"errors"
"fmt"
)

var errNotFound error = errors.New("Not found error")

func main() {
fmt.Printf("error: %v", errNotFound)
}
// error: Not found error

可以把它用于计算平方根函数的参数测试:

1
2
3
4
5
6
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New ("math - square root of negative number")
}
// implementation of Sqrt
}

你可以像下面这样调用 Sqrt 函数:

1
2
3
if f, err := Sqrt(-1); err != nil {
fmt.Printf("Error: %s\n", err)
}

由于 fmt.Printf 会自动调用 String() 方法 (参见 10.7 节),所以错误信息 “Error: math - square root of negative number” 会打印出来。通常(错误信息)都会有像 “Error:” 这样的前缀,所以你的错误信息不要以大写字母开头。

在大部分情况下自定义错误结构类型很有意义的,可以包含除了(低层级的)错误信息以外的其它有用信息,例如,正在进行的操作(打开文件等),全路径或名字。看下面例子中 os.Open 操作触发的 PathError 错误:

1
2
3
4
5
6
7
8
9
10
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string // "open", "unlink", etc.
Path string // The associated file.
Err error // Returned by the system call.
}

func (e *PathError) String() string {
return e.Op + " " + e.Path + ": "+ e.Err.Error()
}

如果有不同错误条件可能发生,那么对实际的错误使用类型断言或类型判断(type-switch)是很有用的,并且可以根据错误场景做一些补救和恢复操作。

1
2
3
4
//  err != nil
if e, ok := err.(*os.PathError); ok {
// remedy situation
}

或:

1
2
3
4
5
6
7
8
9
switch err := err.(type) {
case ParseError:
PrintParseError(err)
case PathError:
PrintPathError(err)
...
default:
fmt.Printf("Not a special error, just %s\n", err)
}

作为第二个例子考虑用 json 包的情况。当 json.Decode 在解析 JSON 文档发生语法错误时,指定返回一个 SyntaxError 类型的错误:

1
2
3
4
5
6
7
type SyntaxError struct {
msg string // description of error
// error occurred after reading Offset bytes, from which line and columnnr can be obtained
Offset int64
}

func (e *SyntaxError) String() string { return e.msg }

在调用代码中你可以像这样用类型断言测试错误是不是上面的类型:

1
2
3
4
if serr, ok := err.(*json.SyntaxError); ok {
line, col := findLine(f, serr.Offset)
return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
}

包也可以用额外的方法(methods)定义特定的错误,比如 net.Error:

1
2
3
4
5
package net
type Error interface {
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}

15.1 节 我们可以看到怎么使用它。

正如你所看到的一样,所有的例子都遵循同一种命名规范:错误类型以 “Error” 结尾,错误变量以 “err” 或 “Err” 开头。

syscall 是低阶外部包,用来提供系统基本调用的原始接口。它们返回封装整数类型错误码的syscall.Errno;类型 syscall.Errno 实现了 Error 接口。

大部分 syscall 函数都返回一个结果和可能的错误,比如:

1
2
3
4
r, err := syscall.Open(name, mode, perm)
if err != nil {
fmt.Println(err.Error())
}

os 包也提供了一套像 os.EINAL 这样的标准错误,它们基于 syscall 错误:

1
2
3
4
5
6
7
8
var (
EPERM Error = Errno(syscall.EPERM)
ENOENT Error = Errno(syscall.ENOENT)
ESRCH Error = Errno(syscall.ESRCH)
EINTR Error = Errno(syscall.EINTR)
EIO Error = Errno(syscall.EIO)
...
)

13.1.2 用 fmt 创建错误对象

通常你想要返回包含错误参数的更有信息量的字符串,例如:可以用 fmt.Errorf() 来实现:它和 fmt.Printf() 完全一样,接收一个或多个格式占位符的格式化字符串和相应数量的占位变量。和打印信息不同的是它用信息生成错误对象。

比如在前面的平方根例子中使用:

1
2
3
if f < 0 {
return 0, fmt.Errorf("math: square root of negative number %g", f)
}

第二个例子:从命令行读取输入时,如果加了 help 标志,我们可以用有用的信息产生一个错误:

1
2
3
4
if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
err = fmt.Errorf("usage: %s infile.txt outfile.txt", filepath.Base(os.Args[0]))
return
}

链接


免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com

推荐阅读:

文章标题:go入门教程- 13.1 错误处理

本文作者:知识铺

发布时间:2019-10-15, 22:30:20

最后更新:2019-10-16, 21:00:37

原始链接:https://blog.zshipu.com/2019/10/15/golang/20191015/13.1/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏