单值上下文中的多个值

Posted

技术标签:

【中文标题】单值上下文中的多个值【英文标题】:Multiple values in single-value context 【发布时间】:2015-03-29 09:49:52 【问题描述】:

由于 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

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

【问题讨论】:

item 通常为nil,以防出现错误。如果不先检查错误,您的代码会在这种情况下崩溃。 【参考方案1】:

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

如果其中一个是error,它的存在是有原因(这是函数可能失败),你应该 em> 绕过它,因为如果你这样做了,你的后续代码可能也会失败(例如导致运行时恐慌)。

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

例子:

// "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,您可以创建一个不会返回error 但会提高运行时间的帮助器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

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

how to parse multiple returns in golang

Return map like 'ok' in Golang on normal functions

【讨论】:

【参考方案2】:

是的,有。

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

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

您可以从this article 阅读更多关于其背后的科学知识。致谢作者。

【讨论】:

【参考方案3】:

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

您可以使用一些技术来延迟错误处理,请参阅 Rob Pike 的 Errors are values。

ew := &errWriterw: 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 的调用。

【讨论】:

【参考方案4】:

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

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

【讨论】:

【参考方案5】:

这样怎么样?

package main

import (
    "fmt"
    "errors"
)

type Item struct 
    Value int
    Name string


var items []Item = []ItemValue: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]
    

【讨论】:

【参考方案6】:

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

func assumeNoError(value interface, err error) interface 
    if err != nil 
        panic("error encountered when none assumed:" + err.Error())
    
    return value

由于它以 interface 的形式返回,因此您通常需要将其转换回函数的返回类型。

例如,OP 的示例名为Get(1),它返回(Item, error)

item := assumeNoError(Get(1)).(Item)

使这成为可能的技巧:Multi-values returned from one function call can be passed in as multi-variable arguments to another function.

作为一种特殊情况,如果一个函数或方法 g 的返回值数量相等并且可以单独分配给另一个函数或方法 f 的参数,则调用 f(g(parameters_of_g)) 将在绑定后调用 f将g的返回值依次传递给f的参数。


这个答案大量借鉴了现有答案,但没有一个提供这种形式的简单通用解决方案。

【讨论】:

以上是关于单值上下文中的多个值的主要内容,如果未能解决你的问题,请参考以下文章

数据的单值多值派生简单复合属性

React - 如何使用上下文从对象中应用多个值

Nextjs:如何使用具有多个值的 ContextAPI,所有这些值都需要从子组件中更新

如何传递多个值以响应本机上下文 API

strconv.Atoi() 给定字符串时抛出错误

从 django 中的单个视图呈现多个模板