Go错误处理技术

huangapple go评论88阅读模式
英文:

Go Error Handling Techniques

问题

我刚开始学习Go语言。我的代码开始有很多这样的结构:

   if err != nil {
      //处理错误
   }

或者这样的结构

  if err := rows.Scan(&some_column); err != nil {
      //处理错误
  }

在Go语言中,有一些良好的惯用法/策略/最佳实践来检查和处理错误吗?

编辑以澄清:我并不是在抱怨或建议Go团队提出更好的解决方案。我想知道我是否做得对,或者是否错过了社区提出的某些技术。谢谢大家。

英文:

I'm just getting started with Go. My code is starting to have a lot of this:

   if err != nil {
      //handle err
   }

or this

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

Are there some good idioms/strategies/best-practices for checking and handling errors in Go?

EDIT to clarify: I'm not bellyaching or suggesting that the Go team come up with something better. I'm asking if I'm doing it right or have I missed some technique that the community came up with. Thanks all.

答案1

得分: 63

你的代码风格很符合惯用语,并且在我看来,这是最佳实践。当然,有些人可能持不同意见,但我认为这种风格在Go语言的标准库中随处可见。换句话说,Go语言的作者们都是以这种方式编写错误处理的。

英文:

Your code is idiomatic and in my opinion it is the best practice available. Some would disagree for sure, but I would argue that this is the style seen all over the standard libraries in Golang. In other words, Go authors write error handling in this way.

答案2

得分: 32

六个月后,Rob Pike写了一篇名为Errors are Values的博客文章。

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

> 当然,涉及错误值的常见语句是测试它是否为nil,但还有无数其他事情可以用错误值做,应用其中一些可以使你的程序更好,消除了如果每个错误都用一个机械的if语句检查所产生的样板代码。
>
>...
>
>利用语言简化错误处理。

>但请记住:无论你做什么,始终要检查错误!

这是一篇很好的阅读材料。

英文:

Six months after this question was asked Rob Pike wrote a blog post titled Errors are Values.

In there he argues that you don't need to program in the way presented by the OP, and mentions several places in the standard library where they use a different pattern.

> Of course a common statement involving an error value is to test whether it is nil, but there are countless other things one can do with an error value, and application of some of those other things can make your program better, eliminating much of the boilerplate that arises if every error is checked with a rote if statement.
>
>...
>
>Use the language to simplify your error handling.
>
>But remember: Whatever you do, always check your errors!

It's a good read.

答案3

得分: 22

我同意jnml的答案,他们都是惯用的代码,并补充如下:

你的第一个例子:

if err != nil {
      //处理错误
}

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

val, err := someFunc()
if err != nil {
      //处理错误
}
//使用val进行操作

你的第二个例子是处理err值时的简写方式。这适用于函数只返回一个error,或者你有意忽略除error之外的返回值。例如,这在返回写入的字节数(有时是不必要的信息)和errorReaderWriter函数中有时会使用:

if _, err := f.Read(file); err != nil {
      //处理错误
}
//使用f进行操作

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

所以就最佳实践而言,据我所知(除了在需要时使用"errors"包创建新错误),你已经涵盖了Go中有关错误的几乎所有知识!

编辑:如果你发现你真的无法没有异常,你可以使用deferpanicrecover来模拟异常。

英文:

I would agree with jnml's answer that they are both idiomatic code, and add the following:

Your first example:

if err != nil {
      //handle err
}

is more idiomatic when dealing with more than one return value. for example:

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

Your second example is nice shorthand when only dealing with the err value. This applies if the function only returns an error, or if you deliberately ignore the returned values other than the error. As an example, this is sometimes used with the Reader and Writer functions that return an int of the number of bytes written (sometimes unnecessary information) and an error:

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

The second form is referred to as using an if initialization statement.

So with regards to best practices, as far as I know (except for using the "errors" package to create new errors when you need them) you've covered pretty much everything you need to know abut errors in Go!

EDIT: If you find you really can't live without exceptions, you can mimic them with defer,panic & recover.

答案4

得分: 3

我为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,  // 读取json(返回json和错误)
    )(
        // q.V将上一个函数调用的json传递下来
        json.Unmarshal, q.V, u,   // 从上面解析json(返回错误)
    )(
        u.Validate,               // 验证用户(返回错误)
    )(
        u.Save,                   // 保存用户(返回错误)
    )(
        ok, w,                    // 发送“ok”消息(不返回错误)
    ).Run()

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

请注意,由于使用了反射,这会带来一些性能开销。

此外,这不是典型的Go代码,所以你可能想在自己的项目中使用它,或者在团队一致同意使用时使用它。

英文:

I made a library for streamlined error handling and piping through a queue of Go functions.

You can find it here: https://github.com/go-on/queue

It has a compact and a verbose syntactic variant.
Here is an example for the short syntax:

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:
           ...
       }
    }
}

Please be aware that there is a little performance overhead, since it
makes use of reflection.

Also this is not idiomatic go code, so you will want to use it in your own projects, or if your team agrees on
using it.

答案5

得分: 3

Go神们已经发布了Go 2中错误处理的“草案设计”。它旨在改变错误的习惯用法:

概述设计

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

反馈维基

简而言之,它看起来像这样:

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

更新:草案设计收到了很多批评,因此我起草了Go 2错误处理需考虑的要求,其中列出了可能的解决方案。

英文:

The Go gods have published a "draft design" for error handling in Go 2. It aims to change the errors idiom:

Overview and Design

They want feedback from users!

Feedback wiki

Briefly, it looks like:

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

UPDATE: The draft design has received a lot of criticism, so I drafted Requirements to Consider for Go 2 Error Handling with a menu of possibilities for an eventual solution.

答案6

得分: 2

在golang和其他语言中,处理错误的一种“策略”是将错误持续传递到调用栈的较高位置,直到能够处理该错误为止。如果你尝试过早处理该错误,那么很可能会导致重复代码。如果处理得太晚,那么可能会破坏你的代码。Golang使这个过程变得非常简单,因为它清楚地表明你是否在给定位置处理错误或传递错误。

如果你要忽略错误,一个简单的下划线_将非常清楚地显示这一事实。如果你要处理它,那么你将在if语句中检查错误的具体情况。

正如上面所说,错误实际上只是一个普通的值。这样对待它。

英文:

A "strategy" for handling errors in golang and in other languages is to continuously propogate errors up the call stack until you are high enough in the call stack to handle that error. If you tried handling that error too early, then you will likely end up repeating code. If you handle it too late, then you will break something in your code. Golang makes this process super easy as it makes it super clear whether you are handling an error at a given location or propagating it up.

If you are going to be ignoring the error, a simple _ will reveal this fact very clearly. If you are handling it, then exactly which case of the error you are handling is clear as you will check for it in the if statement.

Like people said above, an error is actually just a normal value. This treats it as such.

答案7

得分: 1

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

英文:

Most in industry, are following standard rules mentioned in golang documentation Error handling and Go.
And it also helps doc generation for project.

答案8

得分: 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
	}

这不是万能的解决方案,缺点是如果有多个错误,你只能获取到最后一个错误。

但在这种情况下,重复性和风险较低,因此我可以只获取最后一个可能的错误。

英文:

Below is my take on reducing error handling on for Go, sample is for when getting HTTP URL parameters :

(Design pattern derived from 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
}

calling it for multiple possible parameters would be as below:

    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
	}

This is not a silver bullet, the downside is if you had multiple errors you are only able to get the last error.

But in this instance it is relatively repetitive and low risk, hence I can just get the last possible error.

答案9

得分: -1

你可以为类似的错误清理错误处理代码(因为错误是值,所以在这里要小心),并编写一个函数,将错误传递给该函数以处理错误。然后,你就不必每次都写“if err!=nil {}”了。再次强调,这只会清理代码,但我不认为这是一种惯用的做法。

再次强调,仅仅因为你可以这样做,并不意味着你应该这样做。

英文:

You can clean up your error handling code for similar errors (since errors are values you have to be careful here) and write a function which you call with the error passed in to handle the error. You won't have to write "if err!=nil {}" every time then. Again, this will only result in cleaning up the code, but I don't think it is the idiomatic way of doing things.

Again, just because you can doesn't mean you should.

答案10

得分: -1

goerr允许使用函数处理错误

package main

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

func ok(err error) {
    if err != nil {
        goerr.Return(err)
        // 将错误从do_somethingN()返回到main()
        // sequence()终止
    }
}

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

    return nil /// 1,2,3成功
}
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)

    // 处理错误

    fmt.Println(err_do_something)
}
英文:

goerr allows to handle errors with functions

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)
}

答案11

得分: -4

如果你想要精确控制错误,这可能不是解决方案,但对我来说,大部分时间,任何错误都是一个停止器。

所以,我使用函数。

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

fi, err := os.Open("mmm.txt")
Err(err)
英文:

If you want precise control of errors, this may not be the solution, but for me, most of the time, any error is a show stopper.

So, I use functions instead.

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

fi, err := os.Open("mmm.txt")
Err(err)

huangapple
  • 本文由 发表于 2013年6月6日 21:23:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/16963298.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定