在Go中处理多个错误

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

Handling multiple errors in go

问题

我对Go语言还不熟悉,发现错误处理非常冗长。我已经阅读了其中的原因,并且大部分都同意,但是有几个地方似乎处理错误的代码比实际工作的代码还多。这是一个(人为制造的)例子,我将“Hello world!”输入到cat命令中,并读取并打印输出。基本上每一行都有三行代码来处理错误,而且我实际上并没有处理任何错误。

有没有一种惯用的、简洁的处理方式?我只是觉得我可能漏掉了什么。

英文:

I'm new to go and finding the error handling to be extremely verbose. I've read the reasoning for it and mostly agree, but there are a few places where it seems like there's more code to handle errors than actually do the work. Here is a (contrived) example, where I pipe "Hello world!" into cat and read and print the output. Basically every line has three more to handle the error, and I'm not really even handling anything.

package main

import "fmt"
import "io"
import "io/ioutil"
import "os/exec"


func main() {
    cmd := exec.Command("cat", "-")
    stdin, err := cmd.StdinPipe()
    if err != nil {
        return
    }
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        return
    }
    err = cmd.Start()
    if err != nil {
        return
    }
    _, err = io.WriteString(stdin, "Hello world!")
    if err != nil {
        return
    }
    err = stdin.Close();
    if err != nil {
        return
    }
    output, err := ioutil.ReadAll(stdout)
    if err != nil {
        return
    }
    fmt.Println(string(output))
    return
}

Is there an idiomatic, clean way to handle this? I just feel like I'm missing something.

答案1

得分: 7

明显地,我们必须处理任何错误;我们不能简单地忽略它们。

例如,试图使示例更加真实,

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
)

func piping(input string) (string, error) {
	cmd := exec.Command("cat", "-")
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return "", err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return "", err
	}
	err = cmd.Start()
	if err != nil {
		return "", err
	}
	_, err = io.WriteString(stdin, input)
	if err != nil {
		return "", err
	}
	err = stdin.Close()
	if err != nil {
		return "", err
	}
	all, err := ioutil.ReadAll(stdout)
	output := string(all)
	if err != nil {
		return output, err
	}
	return output, nil
}

func main() {
	in := "Hello world!"
	fmt.Println(in)
	out, err := piping(in)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Println(out)
}

输出:

Hello world!
Hello world!

错误处理和Go

在Go中,错误处理很重要。该语言的设计和约定鼓励您在错误发生的地方显式检查错误(与其他语言中抛出异常并有时捕获异常的约定不同)。在某些情况下,这使得Go代码冗长。

英文:

Clearly, we must handle any errors; we can't just ignore them.

For example, trying to make the example less artificial,

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
)

func piping(input string) (string, error) {
	cmd := exec.Command("cat", "-")
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return "", err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return "", err
	}
	err = cmd.Start()
	if err != nil {
		return "", err
	}
	_, err = io.WriteString(stdin, input)
	if err != nil {
		return "", err
	}
	err = stdin.Close()
	if err != nil {
		return "", err
	}
	all, err := ioutil.ReadAll(stdout)
	output := string(all)
	if err != nil {
		return output, err
	}
	return output, nil
}

func main() {
	in := "Hello world!"
	fmt.Println(in)
	out, err := piping(in)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Println(out)
}

Output:

Hello world!
Hello world!

> Error Handling and Go
>
> In Go, error handling is important. The language's design and
> conventions encourage you to explicitly check for errors where they
> occur (as distinct from the convention in other languages of throwing
> exceptions and sometimes catching them). In some cases this makes Go
> code verbose.

答案2

得分: 0

对于成语,可以参考peterSO的答案,该答案开始涉及返回错误的主题,并且可以通过在应用程序中的调用上下文中包装错误来进一步处理。

可能有一些情况下,对操作进行迭代运行可能需要更通用的处理方式,以下链接中提供了一些非常有创意的例子,但正如我在那个问题上的评论中所说,那个代码示例是一个不好的例子:https://stackoverflow.com/questions/15397419/go-handling-multiple-errors-elegantly

无论如何,仅看你提供的示例,这只是在main函数中的一次性操作,所以如果你只是想在交互式Python控制台中玩耍,就像对待这样的操作一样。

英文:

For idiomatic, refer to peterSO's answer which begins to touch on the subject of returning errors, and this can be taken further by wrapping errors with some extra bit of information related to the context of the call within your application.

There may be cases where iterative runs over an operation might warrant something more generalized with some unusually creative examples in the following link, but as I commented on that question, it was a bad code example to examine: https://stackoverflow.com/questions/15397419/go-handling-multiple-errors-elegantly

Regardless, looking solely at the example you have, this is nothing more than a one-off in main, so treat it like such if you're just looking to mess around as one might do in an interactive python console.

package main

import (
    "fmt"
    "io"
    "io/ioutil"
    "os/exec"
)

func main() {
    cmd := exec.Command("cat", "-")
    stdin, _ := cmd.StdinPipe()
    stdout, _ := cmd.StdoutPipe()

    cmd.Start()
    io.WriteString(stdin, "Hello world!")

    stdin.Close();
    output, _ := ioutil.ReadAll(stdout)

    fmt.Println(string(output))
}

答案3

得分: 0

我刚刚用Go写了几百行代码,所以我没有资格指出任何惯用的方法。
然而,在重复的调用和检查错误步骤的情况下,我发现如果我反转逻辑,代码写起来更容易,阅读起来也更容易:而不是检查退出条件(err != nil),我检查继续条件(err == nil),如下所示。
如果您有一种处理错误的唯一方法,无论是返回给调用者还是打印/记录错误,都可以这样做。
这种方法的缺点是,您不能隐式地使用:=声明变量,因为它们将具有分配它们的if块的作用域。

func main() {
    var output []byte
    var stdin io.WriteCloser 
    var stdout io.Reader 

    cmd := exec.Command("cat", "-")

    stdin, err := cmd.StdinPipe()

    if err == nil {
      stdout, err = cmd.StdoutPipe()
    }

    if err == nil {
      err = cmd.Start()
    }

    if err == nil {
      _, err = io.WriteString(stdin, "Hello world!")
    }

    if err == nil {
    output, err = ioutil.ReadAll(stdout)
    } 

    if err == nil {
     err = stdin.Close();
    }

    if err == nil {
	        fmt.Println(string(output))
    } else {
         fmt.Println(string(err.Error())) // 错误处理在这里
    }

    return
}
英文:

I just wrote a few hundreds lines in Go, so I'm not titled to indicate any idiomatic way.
However, in case of repetitive call-and-check-error steps , I find that the code is a bit easier to write and to read as well if I revert the logic : instead of checking the condition for exiting (err != nil ), I check the condition for continuing ( err == nil ), like shown below.
This can be done, if you have an unique way to handle the error, no matter which error is, like returning to the caller or printing/logging it.
The drawback to this approach is that you can't implicitly declare the variables with :=, because they would have the scope of the if block in which they are assigned.

func main() {
    var output []byte
    var stdin io.WriteCloser 
    var stdout io.Reader 

    cmd := exec.Command("cat", "-")

    stdin, err := cmd.StdinPipe()

    if err == nil {
      stdout, err = cmd.StdoutPipe()
    }

    if err == nil {
      err = cmd.Start()
    }

    if err == nil {
      _, err = io.WriteString(stdin, "Hello world!")
    }

    if err == nil {
    output, err = ioutil.ReadAll(stdout)
    } 

    if err == nil {
     err = stdin.Close();
    }

    if err == nil {
	        fmt.Println(string(output))
    } else {
         fmt.Println(string(err.Error())) // Error handling goes here
    }

    return
}

答案4

得分: -2

在这种情况下,我通常会稍微整理一下。

func myFunc() (err error) {
    cmd := exec.Command("cat", "-")

    stdin,  err := cmd.StdinPipe();                  if err != nil { return }
    stdout, err := cmd.StdoutPipe();                 if err != nil { return }

       err  = cmd.Start();                           if err != nil { return }
    _, err  = io.WriteString(stdin, "Hello world!"); if err != nil { return }
       err  = stdin.Close();                         if err != nil { return }
    o, err := ioutil.ReadAll(stdout);                if err != nil { return }

    fmt.Println(string(o))
    return
}

仍然不太美观,但至少不那么垂直,并且我们得到了一些对齐。

我不能说这遵循任何约定,但在我看来,它要容易阅读得多。

英文:

In a situation like this, I usually just flatten it out a bit.

func myFunc() (err error) {
    cmd := exec.Command("cat", "-")

    stdin,  err := cmd.StdinPipe();                  if err != nil { return }
    stdout, err := cmd.StdoutPipe();                 if err != nil { return }

       err  = cmd.Start();                           if err != nil { return }
    _, err  = io.WriteString(stdin, "Hello world!"); if err != nil { return }
       err  = stdin.Close();                         if err != nil { return }
    o, err := ioutil.ReadAll(stdout);                if err != nil { return }

    fmt.Println(string(o))
    return
}

Still ugly, but at least it's less vertical, and we get some alignment.

I can't say this adheres to any sort of convention, but it's a whole lot easier to read IMO.

huangapple
  • 本文由 发表于 2013年4月21日 07:01:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/16126102.html
匿名

发表评论

匿名网友

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

确定