Go 单值上下文中的多个值

Go 单值上下文中的多个值,go,error-handling,return-value,multiple-return-values,Go,Error Handling,Return Value,Multiple Return Values,由于Go中的错误处理,我经常使用多个值函数。到目前为止,我处理这个问题的方式非常混乱,我正在寻找编写更干净代码的最佳实践 假设我有以下功能: type Item struct { Value int Name string } func Get(value int) (Item, error) { // some code return item, nil } package main import "fmt" import "strings" func µ(a .

由于Go中的错误处理,我经常使用多个值函数。到目前为止,我处理这个问题的方式非常混乱,我正在寻找编写更干净代码的最佳实践

假设我有以下功能:

type Item struct {
   Value int
   Name string
}

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

  return item, nil
}
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
}
如何将新变量优雅地分配给
item.Value
。在引入错误处理之前,我的函数只返回
,我可以简单地执行以下操作:

val := Get(1).Value
现在我这样做:

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

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

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

有一些技术可以用来延迟错误处理,请参见Rob Pike


在博客文章中的这个例子中,他演示了如何创建一个
errWriter
类型,将错误处理推迟到调用
write

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

我想这方面的一个技巧是返回一个值数组,而不是“item”和“err”,然后直接执行
item,:=Get(1)[0]
但是我不推荐这样做。

这样怎么样

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

对于多值返回函数,在调用函数时不能引用结果特定值的字段或方法

如果其中一个是
错误
,那么它的存在是有原因的(即函数可能会失败),您不应该绕过它,因为如果您这样做,您的后续代码也可能会严重失败(例如,导致运行时恐慌)

但是,在某些情况下,您知道代码在任何情况下都不会失败。在这些情况下,您可以提供一个助手函数(或方法),该函数将丢弃
错误(或在仍然发生时引发运行时死机)。
如果您从代码中为函数提供输入值,并且您知道这些值有效,则可能会出现这种情况。
很好的例子是和包:如果您在编译时提供了有效的模板或regexp,那么您可以确保在运行时对它们进行解析时不会出错。因此,
模板
包提供函数,
regexp
包提供函数:它们不会返回
错误
s,因为它们的预期用途是保证输入有效的地方

示例:

// "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
,则可以创建一个helper
Must()
函数,该函数不会返回
错误,但如果仍然发生,则会引发运行时死机:

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
查看一些有趣/相关的问题:

是的,有

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

type Item struct {
   Value int
   Name string
}

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

  return item, nil
}
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
}

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


你可以阅读更多关于这背后的科学知识。作者的功劳。

这里有一个带有假设检查的通用帮助函数:

func assumeNoError(value interface{}, err error) interface{} {
    if err != nil {
        panic("error encountered when none assumed:" + err.Error())
    }
    return value
}
由于它以
接口{}
的形式返回,因此通常需要将其转换回函数的返回类型

例如,OP的示例名为
Get(1)
,它返回
(项,错误)


让这成为可能的诀窍是:

作为一种特殊情况,如果一个函数或方法g的返回值在数量上相等,并且可以单独分配给另一个函数或方法f的参数,那么调用f(g(parameters_of_g))将在按顺序将g的返回值绑定到f的参数后调用f



此答案大量借用了现有答案,但没有一个提供此表单的简单通用解决方案。

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