1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 20:52:22 +00:00

Merge pull request #28 from lewgun/patch-9

fix typo
This commit is contained in:
chai2010 2018-01-05 12:49:53 +08:00 committed by GitHub
commit be10ffdd88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -27,7 +27,7 @@ if err != nil {
在Go语言中错误被认为是一种可以预期的结果而异常则是一种非预期的结果发生异常可能表示程序中存在BUG或发生了其它不可控的问题。Go语言推荐使用`recover`函数将内部异常转为错误处理,这使得用户可以真正的关心业务相关的错误处理。 在Go语言中错误被认为是一种可以预期的结果而异常则是一种非预期的结果发生异常可能表示程序中存在BUG或发生了其它不可控的问题。Go语言推荐使用`recover`函数将内部异常转为错误处理,这使得用户可以真正的关心业务相关的错误处理。
如果某个接口简单地将所有普通的错误当做异常抛出,将会错误信息杂乱没有价值。就像在`main`函数中直接捕获全部一样,是没有意义的: 如果某个接口简单地将所有普通的错误当做异常抛出,将会使错误信息杂乱没有价值。就像在`main`函数中直接捕获全部一样,是没有意义的:
```go ```go
func main() { func main() {
@ -86,9 +86,9 @@ func CopyFile(dstName, srcName string) (written int64, err error) {
} }
``` ```
`defer`可以让我们在打开文件时马上思考如何关闭文件。不管函数如何返回,文件关闭语句始终会被执行。同时`defer`语句可以保证,即使`io.Copy`发生了异常,文件依然可以安全地关闭。 `defer`可以让我们在打开文件时马上思考如何关闭文件。不管函数如何返回,文件关闭语句始终会被执行。同时`defer`语句可以保证,即使`io.Copy`发生了异常,文件依然可以安全地关闭。
前文我们说到Go语言中的导出函数一般不抛出异常一个未受控的异常可以看作是程序的BUG。但是对于有一些提供类似Web服务的框架而言它们经常需要接入第三方的中间件。因为第三方的中间件是否存在BUG是否会抛出异常Web框架本身是不能确定的。为了提高系统的稳定性Web框架一般会通过`recover`来防御性地捕获所有处理流程中可能产生的异常,然后将异常转为普通的错误返回。 前文我们说到Go语言中的导出函数一般不抛出异常一个未受控的异常可以看作是程序的BUG。但是对于些提供类似Web服务的框架而言它们经常需要接入第三方的中间件。因为第三方的中间件是否存在BUG是否会抛出异常Web框架本身是不能确定的。为了提高系统的稳定性Web框架一般会通过`recover`来防御性地捕获所有处理流程中可能产生的异常,然后将异常转为普通的错误返回。
让我们以JSON解析器为例说明recover的使用场景。考虑到JSON解析器的复杂性即使某个语言解析器目前工作正常也无法肯定它没有漏洞。因此当某个异常出现时我们不会选择让解析器崩溃而是会将panic异常当作普通的解析错误并附加额外信息提醒用户报告此错误。 让我们以JSON解析器为例说明recover的使用场景。考虑到JSON解析器的复杂性即使某个语言解析器目前工作正常也无法肯定它没有漏洞。因此当某个异常出现时我们不会选择让解析器崩溃而是会将panic异常当作普通的解析错误并附加额外信息提醒用户报告此错误。
@ -119,9 +119,9 @@ if _, err := html.Parse(resp.Body); err != nil {
上层用户在遇到错误时,可以很容易从业务层面理解错误发生的原因。但是鱼和熊掌总是很难兼得,在上层用户获得新的错误的同时,我们也丢失了底层最原始的错误类型(只剩下错误描述信息了)。 上层用户在遇到错误时,可以很容易从业务层面理解错误发生的原因。但是鱼和熊掌总是很难兼得,在上层用户获得新的错误的同时,我们也丢失了底层最原始的错误类型(只剩下错误描述信息了)。
为了记录这种错误类型在包装的变迁过程中的信息,我们一般会定义一个辅助的`WrapError`函数用于包装原始的错误同时保留完整的原始错误类型。为了问题定位的方便同时也为了能记录错误发生时的函数调用状态我们很多时候希望在出现致命错误的时候保存完整的函数调用信息。同时为了支持RPC等跨网络的传输我们可能要需要将错误序列号为为类似JSON的数据然后再从表示原始错误的JSON数据中解码恢复错误 为了记录这种错误类型在包装的变迁过程中的信息,我们一般会定义一个辅助的`WrapError`函数用于包装原始的错误同时保留完整的原始错误类型。为了问题定位的方便同时也为了能记录错误发生时的函数调用状态我们很多时候希望在出现致命错误的时候保存完整的函数调用信息。同时为了支持RPC等跨网络的传输我们可能要需要将错误序列化为类似JSON格式的数据然后再从这些数据中将错误解码恢复出来
为此,我们可以定义自己的`github.com/chai2010/errors`包,里面以下的错误类型: 为此,我们可以定义自己的`github.com/chai2010/errors`包,里面以下的错误类型:
```go ```go
@ -287,7 +287,7 @@ func panic(interface{})
func recover() interface{} func recover() interface{}
``` ```
Go语言函数调用的正常流程是函数执行返回语句返回结果在这个流程中是没有异常的因此在这个流程中执行`recover`异常捕获函数始终是返回`nil`。另一种是异常流程: 当函数调用`panic`抛出异常,函数将停止执行后续的普通语句,但是之前注册的`defere`函数调用仍然保证会被正常执行,然后再返回到的调用者。对于当前函数的调用者,因为处理异常状态还没有被捕获,和直接调用`panic`函数的行为类似。在异常发生时,如果在`defer`中执行`recover`调用,它可以捕获触发`panic`时的参数,并且恢复到正常的执行流程。 Go语言函数调用的正常流程是函数执行返回语句返回结果在这个流程中是没有异常的因此在这个流程中执行`recover`异常捕获函数始终是返回`nil`。另一种是异常流程: 当函数调用`panic`抛出异常,函数将停止执行后续的普通语句,但是之前注册的`defer`函数调用仍然保证会被正常执行,然后再返回到的调用者。对于当前函数的调用者,因为处理异常状态还没有被捕获,和直接调用`panic`函数的行为类似。在异常发生时,如果在`defer`中执行`recover`调用,它可以捕获触发`panic`时的参数,并且恢复到正常的执行流程。
在非`defer`语句中执行`recover`调用是初学者常犯的错误: 在非`defer`语句中执行`recover`调用是初学者常犯的错误: