单值上下文中的多个值

Spe*_*her 97 error-handling return-value go multiple-return-values

由于Go中的错误处理,我经常最终得到多个值函数.到目前为止,我管理它的方式非常混乱,我正在寻找编写更清晰代码的最佳实践.

假设我有以下功能:

type Item struct {
   Value int
   Name string
}

func Get(value int) (Item, error) {
  // some code

  return item, nil
}
Run Code Online (Sandbox Code Playgroud)

如何item.Value优雅地分配新变量.在介绍错误处理之前,我的函数刚刚返回item,我可以简单地执行此操作:

val := Get(1).Value
Run Code Online (Sandbox Code Playgroud)

现在我这样做:

item, _ := Get(1)
val := item.Value
Run Code Online (Sandbox Code Playgroud)

有没有办法直接访问第一个返回的变量?

icz*_*cza 74

如果是多值返回函数,则在调用函数时无法引用结果的特定值的字段或方法.

如果他们中的一个是error,它的存在是有原因(这是函数可能会失败),你应该不会绕过它,因为如果你这样做,你后面的代码可能也惨遭失败(如导致运行时的恐慌).

但是,在某些情况下,您可能知道代码在任何情况下都不会失败.在这些情况下,您可以提供一个辅助函数(或方法),它将丢弃error(或者如果仍然发生则引发运行时混乱).
如果您从代码中提供函数的输入值,并且您知道它们有效,则可能出现这种情况.
这方面很好的例子是templateregexp包:如果你在编译时提供有效的模板或正则表达式,你可以确保它们总是可以在运行时被解析而没有错误.由于这个原因,template包提供了Must(t *Template, err error) *Template功能,regexp包提供了MustCompile(str string) *Regexp功能:它们不返回error因为它们的预期用途是保证输入有效的地方.

例子:

// "text" is a valid template, parsing it will not fail
var t = template.Must(template.New("name").Parse("text"))

// `^[a-z]+\[[0-9]+\]$` is a valid regexp, always compiles
var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)
Run Code Online (Sandbox Code Playgroud)

回到你的情况

如果你可以肯定Get()不会产生error某些输入值,你可以创建一个辅助Must()函数,error如果它仍然发生,它将不会返回但会引发运行时混乱:

func Must(i Item, err error) Item {
    if err != nil {
        panic(err)
    }
    return i
}
Run Code Online (Sandbox Code Playgroud)

但是你不应该在所有情况下使用它,只要你确定它成功了.用法:

val := Must(Get(1)).Value
Run Code Online (Sandbox Code Playgroud)

替代/简化

如果将Get()调用合并到辅助函数中,您甚至可以进一步简化它,让我们调用它MustGet:

func MustGet(value int) Item {
    i, err := Get(value)
    if err != nil {
        panic(err)
    }
    return i
}
Run Code Online (Sandbox Code Playgroud)

用法:

val := MustGet(1).Value
Run Code Online (Sandbox Code Playgroud)

看一些有趣/相关的问题:

如何在golang中解析多个返回

在普通函数中返回Golang中的'ok'映射


Kes*_*ion 8

就在这里。

很惊讶吧?您可以使用一个简单的mute函数从多次返回中获取特定值:

package main

import "fmt"
import "strings"

func µ(a ...interface{}) []interface{} {
    return a
}

type A struct {
    B string
    C func()(string)
}

func main() {
    a := A {
        B:strings.TrimSpace(µ(E())[1].(string)),
        C:µ(G())[0].(func()(string)),
    }

    fmt.Printf ("%s says %s\n", a.B, a.C())
}

func E() (bool, string) {
    return false, "F"
}

func G() (func()(string), bool) {
    return func() string { return "Hello" }, true
}
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/IwqmoKwVm-

请注意如何像从切片/数组中一样选择值编号,然后选择类型以获取实际值。

您可以从本文中阅读更多有关其背后科学的信息。感谢作者。


jma*_*ney 7

不,但这是一件好事,因为你应该总是处理你的错误.

您可以使用一些技术来推迟错误处理,请参阅Rob Pike的错误值.

ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
    return ew.err
}
Run Code Online (Sandbox Code Playgroud)

在博客文章的这个例子中,他说明了如何创建一个errWriter延迟错误处理的类型,直到你完成调用write.


Ant*_*ony 5

不,您不能直接访问第一个值。

我想一个 hack 是返回一个值数组而不是“item”和“err”,然后就这样做, item, _ := Get(1)[0] 但我不推荐这样做。