109

我刚刚开始使用 Go。我的代码开始有很多这样的:

   if err != nil {
      //handle err
   }

或这个

  if err := rows.Scan(&some_column); err != nil {
      //handle err
  }

在 Go 中检查和处理错误是否有一些好的习语/策略/最佳实践?

编辑澄清:我不是在抱怨或建议 Go 团队想出更好的东西。我在问我是否做得对,或者我错过了社区提出的一些技术。谢谢大家。

4

11 回答 11

61

您的代码是惯用的,我认为这是可用的最佳实践。有些人肯定会不同意,但我认为这是在 Golang 标准库中随处可见的风格。换句话说,Go 作者以这种方式编写错误处理。

于 2013-06-06T13:31:30.493 回答
31

在被问到这个问题六个月后,Rob Pike 写了一篇题为Errors are Values的博文。

在那里,他认为您不需要按照 OP 提供的方式进行编程,并提到了标准库中使用不同模式的几个地方。

当然,涉及错误值的常见语句是测试它是否为 nil,但是还有无数其他事情可以用错误值做,并且应用其中一些其他事情可以使您的程序更好,消除很多样板如果使用死记硬背的 if 语句检查每个错误,就会出现这种情况。

...

使用该语言来简化您的错误处理。

但请记住:无论您做什么,都要检查您的错误!

这是一个很好的阅读。

于 2015-03-04T04:25:58.373 回答
22

我同意 jnml 的回答,即它们都是惯用代码,并添加以下内容:

你的第一个例子:

if err != nil {
      //handle err
}

在处理多个返回值时更为惯用。例如:

val, err := someFunc()
if err != nil {
      //handle err
}
//do stuff with val

您的第二个示例仅在处理err值时是很好的简写。这适用于函数仅返回 的情况error,或者如果您故意忽略返回的值而不是error. 例如,这有时与ReaderandWriter函数一起使用,这些函数返回int写入的字节数(有时是不必要的信息)和error

if _, err := f.Read(file); err != nil {
      //handle err
}
//do stuff with f

第二种形式称为使用if 初始化语句

因此,关于最佳实践,据我所知(除了在需要时使用“errors”包来创建新错误)您已经涵盖了几乎所有您需要知道的关于 Go 中的错误的知识!

编辑:如果你发现你真的不能没有例外,你可以用defer, panic&recover模仿它们。

于 2013-06-06T14:42:32.717 回答
4

我创建了一个库,用于通过 Go 函数队列简化错误处理和管道。

你可以在这里找到它:https ://github.com/go-on/queue

它有一个紧凑和冗长的句法变体。以下是简短语法的示例:

import "github.com/go-on/queue/q"

func SaveUser(w http.ResponseWriter, rq *http.Request) {
    u := &User{}
    err := q.Q(                      
        ioutil.ReadAll, rq.Body,  // read json (returns json and error)
    )(
        // q.V pipes the json from the previous function call
        json.Unmarshal, q.V, u,   // unmarshal json from above  (returns error)
    )(
        u.Validate,               // validate the user (returns error)
    )(
        u.Save,                   // save the user (returns error)
    )(
        ok, w,                    // send the "ok" message (returns no error)
    ).Run()

    if err != nil {
       switch err {
         case *json.SyntaxError:
           ...
       }
    }
}

请注意,有一点性能开销,因为它使用反射。

此外,这不是惯用的 go 代码,因此您将希望在自己的项目中使用它,或者如果您的团队同意使用它。

于 2014-01-30T06:12:34.893 回答
2

在 golang 和其他语言中处理错误的“策略”是不断地将错误传播到调用堆栈,直到你在调用堆栈中足够高来处理该错误。如果您过早尝试处理该错误,那么您可能最终会重复代码。如果您处理得太晚,那么您将破坏代码中的某些内容。Golang 让这个过程变得超级简单,因为它让你非常清楚你是在给定位置处理错误还是将其传播出去。

如果你要忽略这个错误,一个简单的 _ 将非常清楚地揭示这个事实。如果您正在处理它,那么您正在处理的错误的确切情况是清楚的,因为您将在 if 语句中检查它。

就像上面人们所说的,错误实际上只是一个正常值。这样对待它。

于 2015-11-26T18:48:24.743 回答
2

Go 大神发布了 Go 2 中错误处理的“设计草案”。它旨在改变错误习语:

概述设计

他们希望得到用户的反馈!

反馈维基

简而言之,它看起来像:

func f() error {
   handle err { fmt.Println(err); return err }
   check mayFail()
   check canFail()
}

更新:设计草案受到了很多批评,因此我起草了 Go 2 错误处理需要考虑的需求,并提供了最终解决方案的可能性菜单。

于 2018-09-13T13:06:17.367 回答
1

大多数业内人士都遵循 golang 文档错误处理和 Go中提到的标准规则。它还有助于为项目生成文档。

于 2017-02-10T12:46:21.890 回答
0

下面是我对减少 Go 错误处理的看法,示例用于获取 HTTP URL 参数时:

(设计模式来源于https://blog.golang.org/errors-are-values

type HTTPAdapter struct {
    Error *common.AppError
}

func (adapter *HTTPAdapter) ReadUUID(r *http.Request, param string, possibleError int) uuid.UUID {
    requestUUID := uuid.Parse(mux.Vars(r)[param])
    if requestUUID == nil { 
        adapter.Error = common.NewAppError(fmt.Errorf("parameter %v is not valid", param),
            possibleError, http.StatusBadRequest)
    }
    return requestUUID
}

为多个可能的参数调用它如下:

    adapter := &httphelper.HTTPAdapter{}
    viewingID := adapter.ReadUUID(r, "viewingID", common.ErrorWhenReadingViewingID)
    messageID := adapter.ReadUUID(r, "messageID", common.ErrorWhenReadingMessadeID)
    if adapter.Error != nil {
        return nil, adapter.Error
    }

这不是灵丹妙药,缺点是如果您有多个错误,您只能得到最后一个错误。

但在这种情况下,它相对重复且风险较低,因此我只能得到最后一个可能的错误。

于 2019-06-19T01:28:41.010 回答
-1

可以为类似的错误清理错误处理代码(因为错误是您在此处必须小心的值)并编写一个函数,您可以使用传入的错误来调用该函数来处理错误。你不必每次都写“if err!=nil {}”。同样,这只会导致清理代码,但我认为这不是惯用的做事方式。

同样,仅仅因为你可以并不意味着你应该

于 2015-06-04T10:07:19.020 回答
-1

goerr允许使用函数处理错误

package main

import "github.com/goerr/goerr"
import "fmt"

func ok(err error) {
    if err != nil {
        goerr.Return(err)
        // returns the error from do_somethingN() to main()
        // sequence() is terminated
    }
}

func sequence() error {
    ok(do_something1())
    ok(do_something2())
    ok(do_something3())

    return nil /// 1,2,3 succeeded
}
func do_something1() error { return nil }
func do_something2() error { return fmt.Errorf("2") }
func do_something3() error {
    fmt.Println("DOING 3")
    return nil
}

func main() {
    err_do_something := goerr.OR1(sequence)

    // handle errors

    fmt.Println(err_do_something)
}
于 2015-07-18T20:17:50.740 回答
-4

如果你想精确控制错误,这可能不是解决方案,但对我来说,大多数时候,任何错误都是一个阻碍。

所以,我改用函数。

func Err(err error) {
    if err!=nil {
        fmt.Println("Oops", err)
        os.Exit(1)
    }
}

fi, err := os.Open("mmm.txt")
Err(err)
于 2015-06-16T21:07:36.927 回答