mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 20:52:22 +00:00
commit
cd5e7b910e
@ -14,7 +14,7 @@ if v, ok := m["key"]; ok {
|
|||||||
|
|
||||||
但是导致失败的原因通常不止一种,很多时候用户希望了解更多的错误信息。如果只是用简单的布尔类型的状态值将不能满足这个要求。在C语言中,默认采用一个整数类型的`errno`来表达错误,这样就可以根据需要定义多种错误类型。在Go语言中,`syscall.Errno`就是对应C语言中`errno`类型的错误。在`syscall`包中的接口,如果有返回错误的话,底层也是`syscall.Errno`错误类型。
|
但是导致失败的原因通常不止一种,很多时候用户希望了解更多的错误信息。如果只是用简单的布尔类型的状态值将不能满足这个要求。在C语言中,默认采用一个整数类型的`errno`来表达错误,这样就可以根据需要定义多种错误类型。在Go语言中,`syscall.Errno`就是对应C语言中`errno`类型的错误。在`syscall`包中的接口,如果有返回错误的话,底层也是`syscall.Errno`错误类型。
|
||||||
|
|
||||||
比如我们通过`syscall`包的接口来修改文件的模式时,如果遇到错误我们可以将`err`强制断言为`syscall.Errno`错误类型处理:
|
比如我们通过`syscall`包的接口来修改文件的模式时,如果遇到错误我们可以通过将`err`强制断言为`syscall.Errno`错误类型来处理:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
err := syscall.Chmod(":invalid path:", 0666)
|
err := syscall.Chmod(":invalid path:", 0666)
|
||||||
@ -103,13 +103,13 @@ func ParseJSON(input string) (s *Syntax, err error) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
在标准库中的`json`包,在内部递归解析JSON数据的时候如果遇到错误,会通过抛出异常的方式来快速跳出深度嵌套的函数调用,然后由最外一级的接口通过`recover`捕获`panic`,然后返回相应的错误信息。
|
标准库中的`json`包,在内部递归解析JSON数据的时候如果遇到错误,会通过抛出异常的方式来快速跳出深度嵌套的函数调用,然后由最外一级的接口通过`recover`捕获`panic`,然后返回相应的错误信息。
|
||||||
|
|
||||||
Go语言库的实现习惯: 即使在包内部使用了`panic`,但是在导出函数时会被转化为明确的错误值。
|
Go语言库的实现习惯: 即使在包内部使用了`panic`,但是在导出函数时会被转化为明确的错误值。
|
||||||
|
|
||||||
## 1.7.2 获取错误的上下文
|
## 1.7.2 获取错误的上下文
|
||||||
|
|
||||||
有时候为了方便上层用户理解;很多时候底层实现者会将底层的错误重新包装为新的错误类型返回给用户:
|
有时候为了方便上层用户理解;底层实现者会将底层的错误重新包装为新的错误类型返回给用户:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
if _, err := html.Parse(resp.Body); err != nil {
|
if _, err := html.Parse(resp.Body); err != nil {
|
||||||
@ -119,7 +119,7 @@ if _, err := html.Parse(resp.Body); err != nil {
|
|||||||
|
|
||||||
上层用户在遇到错误时,可以很容易从业务层面理解错误发生的原因。但是鱼和熊掌总是很难兼得,在上层用户获得新的错误的同时,我们也丢失了底层最原始的错误类型(只剩下错误描述信息了)。
|
上层用户在遇到错误时,可以很容易从业务层面理解错误发生的原因。但是鱼和熊掌总是很难兼得,在上层用户获得新的错误的同时,我们也丢失了底层最原始的错误类型(只剩下错误描述信息了)。
|
||||||
|
|
||||||
为了记录这种错误类型在包装的变迁过程中的信息,我们一般会定义一个辅助的`WrapError`函数,用于包装原始的错误,同时保留完整的原始错误类型。为了问题定位的方便,同时也为了能记录错误发生时的函数调用状态,我们很多时候希望在出现致命错误的时候保存完整的函数调用信息。同时,为了支持RPC等跨网络的传输,我们可能要需要将错误序列化为类似JSON格式的数据,然后再从这些数据中将错误解码恢复出来。
|
为了记录这种错误类型在包装的变迁过程中的信息,我们一般会定义一个辅助的`WrapError`函数,用于包装原始的错误,同时保留完整的原始错误类型。为了问题定位的方便,同时也为了能记录错误发生时的函数调用状态,我们很多时候希望在出现致命错误的时候保存完整的函数调用信息。同时,为了支持RPC等跨网络的传输,我们可能要需要将错误序列化为类似JSON格式的数据,然后再从这些数据中将错误解码恢出来。
|
||||||
|
|
||||||
为此,我们可以定义自己的`github.com/chai2010/errors`包,里面是以下的错误类型:
|
为此,我们可以定义自己的`github.com/chai2010/errors`包,里面是以下的错误类型:
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ func FromJson(json string) (Error, error)
|
|||||||
func ToJson(err error) string
|
func ToJson(err error) string
|
||||||
```
|
```
|
||||||
|
|
||||||
`New`用于构建新的错误类型,和标准库中`errors.New`功能类似,但是增加了出错误时的函数调用栈信息。`FromJson`用于从JSON字符串编码的错误中恢复错误对象。`NewWithCode`则是构造一个带错误码的错误,同时也包含出错误时的函数调用栈信息。`Wrap`和`WrapWithCode`则是错误二次包装函数,用于将底层的错误包装为新的错误,但是保留的原始的底层错误信息。这里返回的错误对象都可以直接调用`json.Marshal`将错误编码为JSON字符串。
|
`New`用于构建新的错误类型,和标准库中`errors.New`功能类似,但是增加了出错时的函数调用栈信息。`FromJson`用于从JSON字符串编码的错误中恢复错误对象。`NewWithCode`则是构造一个带错误码的错误,同时也包含出错时的函数调用栈信息。`Wrap`和`WrapWithCode`则是错误二次包装函数,用于将底层的错误包装为新的错误,但是保留的原始的底层错误信息。这里返回的错误对象都可以直接调用`json.Marshal`将错误编码为JSON字符串。
|
||||||
|
|
||||||
我们可以这样使用包装函数:
|
我们可以这样使用包装函数:
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ func returnsError() error {
|
|||||||
|
|
||||||
因此,在处理错误返回值的时候,没有错误的返回值最好直接写为`nil`。
|
因此,在处理错误返回值的时候,没有错误的返回值最好直接写为`nil`。
|
||||||
|
|
||||||
Go语言作为一个强类型语言,不同类型之间必须要显式的转换(而且必须有相同的基础类型)。但是,Go语言中`interface`是一个例外:非接口类型到接口类型,或者是接口类型之间的转换都是隐式的。这是为了支持方便的鸭子面向对象编程,当然会牺牲一定的安全特性。
|
Go语言作为一个强类型语言,不同类型之间必须要显式的转换(而且必须有相同的基础类型)。但是,Go语言中`interface`是一个例外:非接口类型到接口类型,或者是接口类型之间的转换都是隐式的。这是为了支持鸭子类型,当然会牺牲一定的安全性。
|
||||||
|
|
||||||
## 1.7.4 剖析异常
|
## 1.7.4 剖析异常
|
||||||
|
|
||||||
@ -287,7 +287,7 @@ func panic(interface{})
|
|||||||
func recover() interface{}
|
func recover() interface{}
|
||||||
```
|
```
|
||||||
|
|
||||||
Go语言函数调用的正常流程是函数执行返回语句返回结果,在这个流程中是没有异常的,因此在这个流程中执行`recover`异常捕获函数始终是返回`nil`。另一种是异常流程: 当函数调用`panic`抛出异常,函数将停止执行后续的普通语句,但是之前注册的`defer`函数调用仍然保证会被正常执行,然后再返回到的调用者。对于当前函数的调用者,因为处理异常状态还没有被捕获,和直接调用`panic`函数的行为类似。在异常发生时,如果在`defer`中执行`recover`调用,它可以捕获触发`panic`时的参数,并且恢复到正常的执行流程。
|
Go语言函数调用的正常流程是函数执行返回语句返回结果,在这个流程中是没有异常的,因此在这个流程中执行`recover`异常捕获函数始终是返回`nil`。另一种是异常流程: 当函数调用`panic`抛出异常,函数将停止执行后续的普通语句,但是之前注册的`defer`函数调用仍然保证会被正常执行,然后再返回到调用者。对于当前函数的调用者,因为处理异常状态还没有被捕获,和直接调用`panic`函数的行为类似。在异常发生时,如果在`defer`中执行`recover`调用,它可以捕获触发`panic`时的参数,并且恢复到正常的执行流程。
|
||||||
|
|
||||||
在非`defer`语句中执行`recover`调用是初学者常犯的错误:
|
在非`defer`语句中执行`recover`调用是初学者常犯的错误:
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user