Skip to content

Commit

Permalink
add #40
Browse files Browse the repository at this point in the history
  • Loading branch information
smallnest committed Apr 21, 2024
1 parent 3c340c2 commit b9fb1cc
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Go tips from [Phuong Le](https://twitter.com/func25).
| 37 | | |
| 38 | Make your errors clear with fmt.Errorf, don't just leave them bare | smallnest |
| 39 | | |
| 40 | | |
| 40 | Handle errors while using defer to prevent silent failures | smallnest |
| 41 | | |
| 42 | | |
| 43 | | |
Expand Down
37 changes: 37 additions & 0 deletions src/040.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Tip #40 在使用defer时处理错误以防止静默失败

> 原始链接:[Golang Tip #40: Handle errors while using defer to prevent silent failures](https://twitter.com/func25/status/1764997813501608144)
>
有一个隐蔽的/易被忽视的陷阱,许多人都会落入其中:忘记检查延迟调用中的错误:
![](./images/040/1.png)

让我们以上面的代码片段为例。如果文件关闭操作失败(可能是由于写操作未刷新或文件系统出现问题),且这个错误没有被检查,我们就失去了优雅处理故障的机会。现在,仍然使用defer,我们有3种选择:
- 将其作为函数错误处理
- Panic
- 记录日志

Panic或记录日志都很直接,但如何将其作为函数错误处理呢?在这种情况下,使用命名返回值可能是一个简单的解决方案:
![](./images/040/2.png)

或者更简短的方式:
![](./images/040/3.png)

然而,由于需要创建一个匿名函数,这种方法仍然有些冗长,增加了嵌套层次。考虑到大多数延迟调用都涉及关闭资源,比如连接或I/O操作,我们可以使用一个更简洁的一行解决方案来简化,使用io.Closer:
![](./images/040/4.png)

但是这段代码会导致panic,因为当err可能为nil时对其解引用,对吗?

实际上并非如此,这段代码可以正常工作。

(警告,以下是大段的分析)

幸运的是,由于error是一个接口,`nil error`并不意味着它就是其他指针类型(如`*int`)的nil指针。

一个nil(接口)error的结构是{type=nil; value=nil},但它仍然...是一个值,即接口的零值。

当我们在`defer closeWithError(&err, file)`调用中使用`&err`取err的地址时,我们得到的不是一个nil指针。我们得到的是一个指向接口变量的指针,该变量的值为`{type=nil, value=nil}`

所以在`closeWithError`函数中,当我们使用`*err`解引用错误指针来赋予新值时,我们并没有解引用一个nil指针(那会导致panic)。

相反,我们是通过指针修改了一个接口变量的值。
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [Tip #27 原地过滤](./027.md)
- [Tip #30 使用context.WithoutCancel()继续上下文操作](./030.md)
- [Tip #38 使用 fmt.Errorf 使你的错误信息清晰明了,不要让它们过于赤裸](./038.md)
- [Tip #40 在使用defer时处理错误以防止静默失败](./040.md)
- [Tip #44 有意地使用Must函数来停止程序](./044.md)
- [Tip #47 表驱动测试,测试集和并行运行测试](./047.md)
- [Tip #71 用泛型让 sync.Pool 类型安全](./071.md)
Expand Down
Binary file added src/images/040/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/040/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/040/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/040/4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b9fb1cc

Please sign in to comment.