【问题标题】:Handling multiple errors in go在 go 中处理多个错误
【发布时间】:2013-04-20 23:01:43
【问题描述】:

我是新手,发现错误处理非常冗长。我已经阅读了它的推理并且大多同意,但是在某些地方,处理错误的代码似乎比实际工作要多。这是一个(人为的)示例,我在其中管道“Hello world!”进入 cat 并读取并打印输出。基本上每一行都有另外三个来处理错误,而我什至什么都没有处理。

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
}

有没有一种惯用的、干净的方式来处理这个问题?我只是觉得我错过了什么。

【问题讨论】:

标签: error-handling go


【解决方案1】:

显然,我们必须处理任何错误;我们不能忽视它们。

例如,尽量减少人为的例子,

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!

Error Handling and Go

在 Go 中,错误处理很重要。语言的设计和 约定鼓励您明确检查错误 发生(不同于其他语言的投掷习惯) 异常,有时会捕获它们)。在某些情况下,这使得 Go 代码冗长。

【讨论】:

  • 这并不能解决大量重复代码的问题。请参阅question user2303335 linked to 以获得更好的解决方案。
  • @amon:链接问题的答案都不能令人满意。阅读示例 Go source code 以了解核心 Go 库。按照设计,Go 代码会检查错误。自己重写问题中的代码并向我们展示您的想法。
【解决方案2】:

对于惯用语,请参阅peterSO 的答案,该答案开始涉及返回错误的主题,并且可以通过使用与应用程序中的调用上下文相关的一些额外信息来包装错误来进一步处理.

在某些情况下,对操作进行迭代运行可能需要通过以下链接中的一些非同寻常的创意示例进行更概括的操作,但正如我在该问题上评论的那样,这是一个不好检查的代码示例:Go — handling multiple errors elegantly?

不管怎样,只看你的例子,这只不过是一个一次性的 main,所以如果你只是想像在交互式 python 控制台中做的那样乱七八糟地对待它。

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

【讨论】:

  • 为了简洁起见,我的示例是一次性的 main 函数。在我的实际代码中,我不能简单地忽略错误。
  • 简洁可以完全改变答案。如果代码确实是用于 main 的,那么在遇到错误时使用 log.Fatal 将是合适的。如果这是 RPC 调用的一部分,则包装错误并附加其他有用的信息位(我们应该失败吗?我们应该提供我们所拥有的吗?)将是合适的。我评论的重点: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.忽略错误的代码示例是事后的想法,提醒人们可以这样做。
【解决方案3】:

我只是在 Go 中写了几百行,所以我没有标题来指示任何惯用的方式。 但是,在重复调用和检查错误步骤的情况下,如果我恢复逻辑,我发现代码更容易编写和阅读:而不是检查退出条件(错误!= 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())) // Error handling goes here
    }

    return
}

【讨论】:

    【解决方案4】:

    在这种情况下,我通常只是把它弄平一点。

    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
    }
    

    仍然丑陋,但至少它不那么垂直,而且我们得到了一些对齐。

    我不能说这符合任何约定,但它更容易阅读 IMO。

    【讨论】:

    • 第一次将gofmt 应用到你的代码会破坏你的格式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多