go(基础10)——错误处理

1. 错误的定义

错误表示程序中出现了异常情况。比如当我们试图打开一个文件时,文件系统里却并没有这个文件。这就是异常情况,它用一个错误来表示。

在 Go 中,错误一直是很常见的。错误用内建的 error 类型来表示。

就像其他的内建类型(如 int、float64 等),错误值可以存储在变量里、作为函数的返回值等等。

Go 复制代码
package main
 
import (
    "fmt"
    "os"
)
 
func main() {
    f, err := os.Open("/test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(f.Name(), "opened successfully")
}

执行结果:

说明:

如果一个函数或方法返回了错误,按照惯例,错误会作为最后一个值返回。于是 Open 函数也是将 err 作为最后一个返回值。

按照 Go 的惯例,在处理错误时,通常都是将返回的错误与 nil 比较。nil 值表示了没有错误发生,而非 nil 值表示出现了错误。

2. 错误类型

error是一个接口类型。

Go 复制代码
type error interface {  //接口
    Error() string
}

error 有了一个签名为 Error() string 的方法。所有实现该接口的类型都可以当作一个错误类型。Error() 方法给出了错误的描述。

fmt.Println 在打印错误时,会在内部调用 Error() string 方法来得到该错误的描述。

  1. 错误获取

3.1 断言底层结构体类型,使用结构体字段获取更多信息

Open函数的返回值类型如下:

Go 复制代码
// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
    return OpenFile(name, O_RDONLY, 0)
}

PathError是结构体类型,其定义如下:

Go 复制代码
type PathError struct {
    Op   string
    Path string
    Err  error
}
 
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

修改上面的代码:

Go 复制代码
package main
 
import (
    "fmt"
    "os"
)
 
func main() {
    f, err := os.Open("/test.txt")
    if err, ok := err.(*os.PathError); ok {
        fmt.Println("File at path", err.Path, "failed to open")
        return
    }
    fmt.Println(f.Name(), "opened successfully")
}

执行结果:

3.2 断言底层结构体类型,调用方法获取更多信息

第二种获取更多错误信息的方法,也是对底层类型进行断言,然后通过调用该结构体类型的方法,来获取更多的信息。

标准库中的 DNSError 结构体类型定义如下:

Go 复制代码
type DNSError struct {
    ...
}
 
func (e *DNSError) Error() string {
    ...
}
func (e *DNSError) Timeout() bool {
    ...
}
func (e *DNSError) Temporary() bool {
    ...
}

从上述代码可以看到,DNSError 结构体还有 Timeout() bool 和 Temporary() bool 两个方法,它们返回一个布尔值,指出该错误是由超时引起的,还是临时性错误。

接下来我们编写一个程序,断言 *DNSError 类型,并调用这些方法来确定该错误是临时性错误,还是由超时导致的。

Go 复制代码
package main
 
import (
    "fmt"
    "net"
)
 
func main() {
    addr, err := net.LookupHost("golangbot123.com")
    if err, ok := err.(*net.DNSError); ok {   //通过类型断言,获取底层错误值
        if err.Timeout() {
            fmt.Println("operation timed out")
        } else if err.Temporary() {
            fmt.Println("temporary error")
        } else {
            fmt.Println("generic error: ", err)
        }
        return
    }
    fmt.Println(addr)
}

执行结果:

3.3 直接比较

第三种获取错误的更多信息的方式,是与 error 类型的变量直接比较。我们通过一个示例来理解。

filepath 包中的 `Glob`用于返回满足 glob 模式的所有文件名。如果模式写的不对,该函数会返回一个错误 ErrBadPattern。

filepath 包中的 ErrBadPattern 定义如下:

Go 复制代码
var ErrBadPattern = errors.New("syntax error in pattern")

errors.New() 用于创建一个新的错误。

当模式不正确时,Glob 函数会返回 ErrBadPattern。

Go 复制代码
package main
 
import (
    "fmt"
    "path/filepath"
)
 
func main() {
    files, error := filepath.Glob("[")
    if error != nil && error == filepath.ErrBadPattern {
        fmt.Println(error)
        return
    }
    fmt.Println("matched files", files)
}

执行结果:

4. 不可忽略错误

绝不要忽略错误。

Go 复制代码
package main
 
import (
    "fmt"
    "path/filepath"
)
 
func main() {
    files, _ := filepath.Glob("[")
    fmt.Println("matched files", files)
}

执行结果:

由于忽略了错误,输出看起来就像是没有任何匹配了 glob 模式的文件,但实际上这是因为模式的写法不对。所以绝不要忽略错误。

相关推荐
甜鲸鱼1 小时前
【Spring Boot + OpenAPI 3】开箱即用的 API 文档方案(SpringDoc + Knife4j)
java·spring boot·后端
robch1 小时前
Java后端优雅的实现分页搜索排序-架构2
java·开发语言·架构
她说..1 小时前
在定义Java接口参数时,遇到整数类型,到底该用int还是Integer?
java·开发语言·java-ee·springboot
Evand J1 小时前
【PSINS进阶例程】雷达三维跟踪与EKF轨迹滤波。带坐标转换,观测为斜距、方向角、俯仰角。MATLAB编写,附下载链接
开发语言·matlab·psins·雷达观测
雨中散步撒哈拉1 小时前
21、做中学 | 高一上期 |Golang单元测试
golang·单元测试·log4j
专业开发者1 小时前
Android 位置服务(LBS)客户支持指南
开发语言·php
是店小二呀1 小时前
openEuler多核压缩性能实战:从单核到多核的算力跃升
后端
是店小二呀1 小时前
openEuler 上 Redis 性能调优与基准测试实战
后端
是店小二呀1 小时前
openEuler 25.09 系统级开发环境配置与实践(GCC + GDB + SDK)
后端