单值上下文中的多个值


110

由于Go中的错误处理,我经常会遇到多个值函数。到目前为止,我的处理方式非常混乱,我正在寻找编写更简洁代码的最佳实践。

假设我具有以下功能:

type Item struct {
   Value int
   Name string
}

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

  return item, nil
}

我如何item.Value优雅地分配一个新变量。在引入错误处理之前,我的函数刚刚返回item,我可以简单地做到这一点:

val := Get(1).Value

现在,我这样做:

item, _ := Get(1)
val := item.Value

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


3
item通常会nil出现错误。如果不先检查错误,在这种情况下您的代码将崩溃。
2015年

Answers:


83

如果是多值返回函数,则在调用函数时不能引用具有特定结果值的字段或方法。

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

但是,在某些情况下,您知道代码在任何情况下都不会失败。在这些情况下,您可以提供一个辅助函数(或方法),该函数将放弃辅助函数(或方法,error如果仍然发生,则会引起运行时恐慌)。
如果您从代码中提供了函数的输入值,并且知道它们可以工作,则可能是这种情况。和软件包
就是一个很好的例子:如果在编译时提供了有效的模板或regexp,则可以确保它们在运行时始终可以被解析而不会出错。因此,包提供了功能,包提供了功能:它们不返回templateregexptemplateMust(t *Template, err error) *TemplateregexpMustCompile(str string) *Regexperror,因为它们的预期用途是保证输入有效的地方。

例子:

// "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]+\]$`)

回到你的情况

如果可以确定Get()不会error为某些输入值生成,则可以创建一个辅助Must()函数,该函数将不返回,error但如果仍然发生则引发运行时恐慌:

func Must(i Item, err error) Item {
    if err != nil {
        panic(err)
    }
    return i
}

但是,仅当您确定成功时,才应在所有情况下都不要使用此功能。用法:

val := Must(Get(1)).Value

替代/简化

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

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

用法:

val := MustGet(1).Value

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

如何解析golang中的多个收益

使用正常功能在Golang中返回类似“ ok”的地图


7

不,但这是一件好事,因为您应该始终处理错误。

您可以采用一些技术来延迟错误处理,请参阅Robes 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
}

在博客文章的此示例中,他说明了如何创建一个errWriter类型,该类型可以推迟错误处理直到调用完成write


6

就在这里。

令人惊讶,是吗?您可以使用简单的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
}

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

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

您可以从本文中详细了解背后的科学。致谢作者。


5

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

我想对此的一种破解方法是返回一个值数组,而不是返回“ item”和“ err”,然后这样做, item, _ := Get(1)[0] 但是我不建议这样做。


3

这样呢

package main

import (
    "fmt"
    "errors"
)

type Item struct {
    Value int
    Name string
}

var items []Item = []Item{{Value:0, Name:"zero"}, 
                        {Value:1, Name:"one"}, 
                        {Value:2, Name:"two"}}

func main() {
    var err error
    v := Get(3, &err).Value
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(v)

}

func Get(value int, err *error) Item {
    if value > (len(items) - 1) {
        *err = errors.New("error")
        return Item{}
    } else {
        return items[value]
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.