我刚开始使用Go。我的代码开始有很多这样的东西:
if err != nil {
//handle err
}
或这个
if err := rows.Scan(&some_column); err != nil {
//handle err
}
在Go中检查和处理错误是否有一些好的习惯用法/策略/最佳实践?
编辑来澄清:我不是在鼓动或建议Go团队提出更好的建议。我问的是我做对了还是错过了社区提出的一些技巧。谢谢大家
我刚开始使用Go。我的代码开始有很多这样的东西:
if err != nil {
//handle err
}
或这个
if err := rows.Scan(&some_column); err != nil {
//handle err
}
在Go中检查和处理错误是否有一些好的习惯用法/策略/最佳实践?
编辑来澄清:我不是在鼓动或建议Go团队提出更好的建议。我问的是我做对了还是错过了社区提出的一些技巧。谢谢大家
Answers:
提出这个问题六个月后,罗伯·派克(Rob Pike)撰写了一篇题为“ 错误就是价值 ”的博客文章。
他在其中辩称,您无需按照OP提出的方式进行编程,并且提到了标准库中使用不同模式的多个位置。
当然,一个涉及错误值的常见语句是测试它是否为nil,但是使用错误值还有很多其他事情可以使用,而其中一些其他事情的应用可以使您的程序变得更好,从而消除了很多样板如果每个错误都使用了if死记硬背的语句进行检查,则会出现这种情况。
...
使用该语言可以简化错误处理。
但是请记住:无论做什么,都要检查错误!
这是一本好书。
我同意jnml的回答,它们都是惯用代码,并添加以下内容:
您的第一个示例:
if err != nil {
//handle err
}
当处理多个返回值时,这种用法更为惯用。例如:
val, err := someFunc()
if err != nil {
//handle err
}
//do stuff with val
当仅处理err
值时,第二个示例是很好的速记。如果函数仅返回一个error
,或者您故意忽略除以外的返回值,则适用此方法error
。例如,有时与Reader
和Writer
函数一起使用,该函数返回int
写入的字节数(有时是不必要的信息)和的返回值error
:
if _, err := f.Read(file); err != nil {
//handle err
}
//do stuff with f
第二种形式称为使用if初始化语句。
因此,就最佳实践而言,据我所知(除了在需要时使用“错误”包创建新错误之外),您已经了解了Go中几乎所有与错误有关的知识!
编辑:如果你发现你真的不能没有例外,你可以模仿他们defer
,panic
和recover
。
我制作了一个库,用于通过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代码,因此您将希望在自己的项目中使用它,或者如果您的团队同意使用它。
在行业中,大多数人遵循golang文档中的标准规则错误处理和Go。而且还有助于为项目生成文档。
下面是我减少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
}
这不是灵丹妙药,缺点是,如果您有多个错误,则只能得到最后一个错误。
但是在这种情况下,它是相对重复的并且风险较低,因此我只能得到最后一个可能的错误。
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)
}
如果您想精确地控制错误,这可能不是解决方案,但对我而言,大多数情况下,任何错误都是一个表演障碍。
因此,我改用函数。
func Err(err error) {
if err!=nil {
fmt.Println("Oops", err)
os.Exit(1)
}
}
fi, err := os.Open("mmm.txt")
Err(err)
stderr
而不是stdout
,所以只使用log.Fatal(err)
or即可log.Fatalln("some message:", err)
。由于几乎没有什么比main
应该做出这样的决定来结束整个程序(即从函数/方法返回错误,不要中止)的决定了,在极少数情况下,这就是您想要做的事情,这样做更干净,更好地明确地执行它(即if err := someFunc(); err != nil { log.Fatal(err) }
),而不是通过不清楚其操作方式的“帮助程序”功能(名称“ Err”不好,它没有表示可能终止程序)。