diff --git a/translated/tech/20170123 What I Dont Like About Error Handling in Go and How to Work Around It.md b/published/20170123 What I Dont Like About Error Handling in Go and How to Work Around It.md similarity index 85% rename from translated/tech/20170123 What I Dont Like About Error Handling in Go and How to Work Around It.md rename to published/20170123 What I Dont Like About Error Handling in Go and How to Work Around It.md index 9fd5fa6621..dae5a6e6dc 100644 --- a/translated/tech/20170123 What I Dont Like About Error Handling in Go and How to Work Around It.md +++ b/published/20170123 What I Dont Like About Error Handling in Go and How to Work Around It.md @@ -1,18 +1,17 @@ 我对 Go 的错误处理有哪些不满,以及我是如何处理的 ====================== -写 Go 的人往往对它的错误处理模式有一定的看法。按不同的语言经验,人们可能有不同的习惯处理方法。这就是为什么我决定要写这篇文章,尽管有点固执己见,但我认为吸收我的经验是有用的。我想要讲的主要问题是,很难去强制良好的错误处理实践,经常错误没有堆栈追踪,并且错误处理本身太冗长。不过,我已经看到了一些潜在的解决方案,或许能帮助解决一些问题。 +写 Go 的人往往对它的错误处理模式有一定的看法。按不同的语言经验,人们可能有不同的习惯处理方法。这就是为什么我决定要写这篇文章,尽管有点固执己见,但我认为听取我的经验是有用的。我想要讲的主要问题是,很难去强制执行良好的错误处理实践,错误经常没有堆栈追踪,并且错误处理本身太冗长。不过,我已经看到了一些潜在的解决方案,或许能帮助解决一些问题。 ### 与其他语言的快速比较 - [在 Go 中,所有的错误都是值][1]。因为这点,相当多的函数最后会返回一个 `error`, 看起来像这样: ``` func (s *SomeStruct) Function() (string, error) ``` -由于这点,调用代码常规上会使用 `if` 语句来检查它们: +因此这导致调用代码通常会使用 `if` 语句来检查它们: ``` bytes, err := someStruct.Function() @@ -27,7 +26,7 @@ if err != nil { public String function() throws Exception ``` - `try-catch` 而不是 `if err != nil`: +它使用的是 `try-catch` 而不是 `if err != nil`: ``` try { @@ -43,7 +42,7 @@ catch (Exception e) { ### 实现集中式错误处理 -退一步,让我们看看为什么以及如何在一个集中的地方处理错误。 +退一步,让我们看看为什么要在一个集中的地方处理错误,以及如何做到。 大多数人或许会熟悉的一个例子是 web 服务 - 如果出现了一些未预料的的服务端错误,我们会生成一个 5xx 错误。在 Go 中,你或许会这么实现: @@ -68,7 +67,7 @@ func viewCompanies(w http.ResponseWriter, r *http.Request) { } ``` -这并不是一个好的解决方案,因为我们不得不重复在所有的处理函数中处理错误。为了能更好地维护,最好能在一处地方处理错误。幸运的是,[在 Go 的博客中,Andrew Gerrand 提供了一个替代方法][2],可以完美地实现。我们可以创建一个处理错误的 Type: +这并不是一个好的解决方案,因为我们不得不重复地在所有的处理函数中处理错误。为了能更好地维护,最好能在一处地方处理错误。幸运的是,[在 Go 语言的官方博客中,Andrew Gerrand 提供了一个替代方法][2],可以完美地实现。我们可以创建一个处理错误的 Type: ``` type appHandler func(http.ResponseWriter, *http.Request) error @@ -89,11 +88,11 @@ func init() { } ``` -接着我们需要做的是修改处理函数的签名来使它们返回 `errors`。这个方法很好,因为我们做到了 [dry][3] 原则,并且没有重复使用不必要的代码 - 现在我们可以在一处返回默认错误了。 +接着我们需要做的是修改处理函数的签名来使它们返回 `errors`。这个方法很好,因为我们做到了 [DRY][3] 原则,并且没有重复使用不必要的代码 - 现在我们可以在单独一个地方返回默认错误了。 ### 错误上下文 -在先前的例子中,我们可能会收到许多潜在的错误,它们的任何一个都可能在调用堆栈的许多部分生成。这时候事情就变得棘手了。 +在先前的例子中,我们可能会收到许多潜在的错误,它们中的任何一个都可能在调用堆栈的许多环节中生成。这时候事情就变得棘手了。 为了演示这点,我们可以扩展我们的处理函数。它可能看上去像这样,因为模板执行并不是唯一一处会发生错误的地方: @@ -109,9 +108,9 @@ func viewUsers(w http.ResponseWriter, r *http.Request) error { 调用链可能会相当深,在整个过程中,各种错误可能在不同的地方实例化。[Russ Cox][4]的这篇文章解释了如何避免遇到太多这类问题的最佳实践: - “在 Go 中错误报告的部分约定是函数包含相关的上下文,包括正在尝试的操作(比如函数名和它的参数)。” +> “在 Go 中错误报告的部分约定是函数包含相关的上下文,包括正在尝试的操作(比如函数名和它的参数)。” -给出的例子是对 OS 包的一个调用: +这个给出的例子是对 OS 包的一个调用: ``` err := os.Remove("/tmp/nonexist") @@ -140,11 +139,11 @@ if err != nil { } ``` -这意味着错误何时发生并没有传递出来。 +这意味着错误何时发生并没有被传递出来。 应该注意的是,所有这些错误都可以在 `Exception` 驱动的模型中发生 - 糟糕的错误信息、隐藏异常等。那么为什么我认为该模型更有用? -如果我们在处理一个糟糕的异常消息,_我们仍然能够了解它发生在调用堆栈中什么地方_。因为堆栈跟踪,这引发了一些我对 Go 不了解的部分 - 你知道 Go 的 `panic` 包含了堆栈追踪,但是 `error` 没有。我推测可能是 `panic` 会使你的程序崩溃,因此需要一个堆栈追踪,而处理错误并不会,因为它会假定你在它发生的地方做一些事。 +即便我们在处理一个糟糕的异常消息,_我们仍然能够了解它发生在调用堆栈中什么地方_。因为堆栈跟踪,这引发了一些我对 Go 不了解的部分 - 你知道 Go 的 `panic` 包含了堆栈追踪,但是 `error` 没有。我推测可能是 `panic` 会使你的程序崩溃,因此需要一个堆栈追踪,而处理错误并不会,因为它会假定你在它发生的地方做一些事。 所以让我们回到之前的例子 - 一个有糟糕错误信息的第三方库,它只是输出了调用链。你认为调试会更容易吗? @@ -174,13 +173,13 @@ LOGGER.error(ex.getMessage()) // 不记录堆栈追踪 LOGGER.error(ex.getMessage(), ex) // 记录堆栈追踪 ``` -但是 Go 似乎设计中就没有这个信息。 +但是 Go 似乎在设计中就没有这个信息。 在获取上下文信息方面 - Russ 还提到了社区正在讨论一些潜在的接口用于剥离上下文错误。关于这点,了解更多或许会很有趣。 ### 堆栈追踪问题解决方案 -幸运的是,在做了一些查找后,我发现了这个出色的[ Go 错误][5]库来帮助解决这个问题,来给错误添加堆栈跟踪: +幸运的是,在做了一些查找后,我发现了这个出色的 [Go 错误][5]库来帮助解决这个问题,来给错误添加堆栈跟踪: ``` if errors.Is(err, crashy.Crashed) { @@ -188,7 +187,7 @@ if errors.Is(err, crashy.Crashed) { } ``` -不过,我认为这个功能如果能成为语言的第一类公民first class citizenship 将是一个改进,这样你就不必对类型做一些修改了。此外,如果我们像先前的例子那样使用第三方库,它可能没有使用 `crashy` - 我们仍有相同的问题。 +不过,我认为这个功能如果能成为语言的第一类公民first class citizenship将是一个改进,这样你就不必做一些类型修改了。此外,如果我们像先前的例子那样使用第三方库,它可能没有使用 `crashy` - 我们仍有相同的问题。 ### 我们对错误应该做什么? @@ -280,7 +279,7 @@ if ew.err != nil { } ``` -这也是一个很好的方案,但是我感觉缺少了点什么 - 因为我们不能重复使用这个模式。如果我们想要一个含有字符串参数的方法,我们就不得不改变函数签名。或者如果我们不想执行写会怎样?我们可以尝试使它更通用: +这也是一个很好的方案,但是我感觉缺少了点什么 - 因为我们不能重复使用这个模式。如果我们想要一个含有字符串参数的方法,我们就不得不改变函数签名。或者如果我们不想执行写操作会怎样?我们可以尝试使它更通用: ``` type errWrapper struct {