Error handling 处理go中的多个错误

Error handling 处理go中的多个错误,error-handling,go,Error Handling,Go,我是新手,发现错误处理非常冗长。我已经阅读了它的理由,大部分人都同意,但是在一些地方,处理错误的代码似乎比实际工作要多。下面是一个(人为的)示例,我将“Hello world!”导入cat,然后读取并打印输出。基本上,每一行都有三行代码来处理错误,我甚至没有处理任何事情 package main import "fmt" import "io" import "io/ioutil" import "os/exec" func main() { cmd := exec.Command

我是新手,发现错误处理非常冗长。我已经阅读了它的理由,大部分人都同意,但是在一些地方,处理错误的代码似乎比实际工作要多。下面是一个(人为的)示例,我将“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
}

有没有一种惯用的、干净的方法来处理这个问题?我只是觉得我遗漏了一些东西。

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

比如说,试图让这个例子不那么人为

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中,错误处理非常重要。语言的设计与应用 约定鼓励您明确检查错误所在的位置 发生(与其他语言的惯例不同) 异常,有时捕获它们)。在某些情况下,这会让你成功 代码冗长


有关惯用用法,请参阅peterSO的回答,其中开始涉及返回错误的主题,这可以通过在应用程序中使用一些与调用上下文相关的额外信息包装错误来进一步理解

在某些情况下,迭代运行一个操作可能会保证在以下链接中使用一些不同寻常的创造性示例来实现更一般化,但正如我对该问题的评论,这是一个不好的代码示例:

不管怎样,仅仅看一下您所拥有的示例,这只是一个一次性的,所以如果您只是想像在交互式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))
}

在这种情况下,我通常只是稍微平复一下

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
}
仍然很难看,但至少它不那么垂直,我们得到了一些校准


我不能说这符合任何形式的惯例,但在我看来,它更容易阅读。

我只是在围棋中写了几百行,所以我没有标题来说明任何惯用的方式。 但是,在重复调用和检查错误步骤的情况下,我发现如果我还原逻辑,代码的编写和读取会更容易一些:我没有检查退出的条件(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())) // Error handling goes here
    }

    return
}

这并不能解决大量重复代码的问题。有关更好的解决方案,请参阅。@amon:链接问题的答案都不令人满意。阅读核心Go库的示例。按照设计,Go代码检查错误。请自己重写问题中的代码,并向我们展示您的想法。为了简洁起见,我的示例是一个一次性的主函数。在我的实际代码中,我不能简单地忽略错误。简洁可以完全改变答案。如果代码确实要发送到main,那么在遇到错误时,使用
log.Fatal
将是合适的。如果这是RPC调用的一部分,那么包装错误并附加其他有用的信息(我们是否失败?我们是否应该提供我们所拥有的?)将是合适的。我评论的重点是:
返回错误,通过在应用程序中使用一些与调用上下文相关的额外信息包装错误,可以进一步实现这一点。
忽略错误的代码示例是事后考虑,提醒您可以这样做。
gofmt
对代码的第一次应用将破坏您的格式设置。