Golang语言基础教程:错误处理

本文介绍了Golang的错误处理机制,包括错误定义、错误类型表示、自定义错误、panic和recover的使用,以及错误处理的最佳实践。通过示例代码展示了如何处理和获取错误信息,强调了不应忽略错误,提倡在代码中显式检查错误。此外,讨论了错误和异常的区别,以及何时使用错误和异常表达。
摘要由CSDN通过智能技术生成

在实际工程项目中,我们希望通过程序的错误信息快速定位问题,但是又不喜欢错误处理代码写的冗余而又啰嗦。Go语言没有提供像JavaC#语言中的try...catch异常处理方式,而是通过函数返回值逐层往上抛。这种设计,鼓励工程师在代码中显式的检查错误,而非忽略错误,好处就是避免漏掉本应处理的错误。但是带来一个弊端,让代码啰嗦。

1.1 什么是错误

错误是什么?

错误指的是可能出现问题的地方出现了问题。比如打开一个文件时失败,这种情况在人们的意料之中 。

而异常指的是不应该出现问题的地方出现了问题。比如引用了空指针,这种情况在人们的意料之外。可见,错误是业务过程的一部分,而异常不是 。

 

Go中的错误也是一种类型。错误用内置的error 类型表示。就像其他类型的,如int,float64,。错误值可以存储在变量中,从函数中返回,等等。

1.2 演示错误

让我们从一个示例程序开始,这个程序尝试打开一个不存在的文件。

示例代码:

package main
​
import (  
    "fmt"
    "os"
)
​
func main() {  
    f, err := os.Open("/test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
  //根据f进行文件的读或写
    fmt.Println(f.Name(), "opened successfully")
}
在os包中有打开文件的功能函数:
​ func Open(name string) (file *File, err error)
如果文件已经成功打开,那么Open函数将返回文件处理。如果在打开文件时出现错误,将返回一个非nil错误。

如果一个函数或方法返回一个错误,那么按照惯例,它必须是函数返回的最后一个值。因此,Open 函数返回的值是最后一个值。

处理错误的惯用方法是将返回的错误与nil进行比较。nil值表示没有发生错误,而非nil值表示出现错误。在我们的例子中,我们检查错误是否为nil。如果它不是nil,我们只需打印错误并从主函数返回。

运行结果:

open /test.txt: No such file or directory

我们得到一个错误,说明该文件不存在。

1.3 错误类型表示

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

让我们再深入一点,看看如何定义错误类型的构建。错误是一个带有以下定义的接口类型,

type error interface {
    Error() string
}

它包含一个带有Error()字符串的方法。任何实现这个接口的类型都可以作为一个错误使用。这个方法提供了对错误的描述。

当打印错误时,fmt.Println函数在内部调用Error() 方法来获取错误的描述。这就是错误描述是如何在一行中打印出来的。

从错误中提取更多信息的不同方法

既然我们知道错误是一种接口类型,那么让我们看看如何提取更多关于错误的信息。

在上面的例子中,我们仅仅是打印了错误的描述。如果我们想要的是导致错误的文件的实际路径。一种可能的方法是解析错误字符串。这是我们程序的输出,

open /test.txt: No such file or directory  

我们可以解析这个错误消息并从中获取文件路径"/test.txt"。但这是一个糟糕的方法。在新版本的语言中,错误描述可以随时更改,我们的代码将会中断。

是否有办法可靠地获取文件名?答案是肯定的,它可以做到,标准Go库使用不同的方式提供更多关于错误的信息。让我们一看一看。

1.断言底层结构类型并从结构字段获取更多信息

如果仔细阅读打开函数的文档,可以看到它返回的是PathError类型的错误。PathError是一个struct类型,它在标准库中的实现如下,

type PathError struct {  
    Op   string
    Path string
    Err  error
}
​
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }  

从上面的代码中,您可以理解PathError通过声明Error()string方法实现了错误接口。该方法连接操作、路径和实际错误并返回它。这样我们就得到了错误信息,

open /test.txt: No such file or directory 

PathError结构的路径字段包含导致错误的文件的路径。让我们修改上面写的程序,并打印出路径。

修改代码:

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")
}

在上面的程序中,我们使用类型断言获得错误接口的基本值。然后我们用错误来打印路径.这个程序输出,

File at path /test.txt failed to open  
  1. 断言底层结构类型,并使用方法获取更多信息

获得更多信息的第二种方法是断言底层类型,并通过调用struct类型的方法获取更多信息。

示例代码:

type DNSError struct {  
    ...
}
​
func (e *DNSError) Error() string {  
    ...
}
func (e *DNSError) Timeout() bool {  
    ... 
}
func (e *DNSError) Temporary() bool {  
    ... 
}

从上面的代码中可以看到,DNSError struct有两个方法Timeout() bool和Temporary() bool,它们返回一个布尔值,表示错误是由于超时还是临时的。

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

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)
}

在上面的程序中,我们正在尝试获取一个无效域名的ip地址,这是一个无效的域名。http://golangbot123.com。我们通过声明它来输入*net.DNSError来获得错误的潜在价值。

在我们的例子中,错误既不是暂时的&#x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值