【问题标题】:How to pass multiple return values to a variadic function?如何将多个返回值传递给可变参数函数?
【发布时间】:2018-10-04 19:23:10
【问题描述】:

我有一个返回两个整数值的 Go 函数。下面是函数

func temp() (int, int){
 return 1,1
}

是否可以将temp 函数直接放入Println 并使用如下字符串格式打印两个输出:

fmt.Println("first= %d and second = %d", temp() ) // This doesn't work

在 Python 中,我可以执行以下操作:

def func():
    return 1,1
print("{0}={1}".format(*func())
>> '1=2'

我也可以在 Go 中做类似的事情吗?

【问题讨论】:

  • 或,但不能同时使用。 := 只是编译器推断分配类型的语法糖。
  • 呃,我不认为“多值表达式解构”与静态类型有任何关系。但我想对一个人来说显而易见的事情对其他人来说并不总是显而易见的,尤其是新手......
  • @RayfenWindspear 是的,这似乎是 go 语言设计者的故意选择,而不是类型系统严格性的强制要求。感谢您的跟进,我不知道该示例是否可行!
  • @maerics 似乎适用于函数采用可变参数接口{} 以及类型完全匹配的情况。直到。 play.golang.org/p/Tbja3KNZO_W
  • 我找到了一篇深度文章,它本身就值得回答,但是在这里只用链接回答问题是无效的,所以我将它放在这里。 medium.com/golangspec/…

标签: go return-value variadic multiple-return-values


【解决方案1】:

前言:我在github.com/icza/gox发布了这个实用程序,见gox.Wrap()


首先,对于您尝试执行的操作,您应该使用 fmt.Printf() 而不是 fmt.Println(),因为只有前者期望并使用格式字符串。

今后,默认情况下不支持此功能,因为引用自 Spec: Calls:

作为一种特殊情况,如果函数或方法g 的返回值数量相等并且可以单独分配给另一个函数或方法f 的参数,则调用f(g(parameters_of_g)) 将调用fg的返回值依次绑定到f的参数后。 f的调用不能包含除g的调用以外的任何参数,并且g必须至少有一个返回值。如果f有一个最终的...参数,它被分配了 g 的返回值,这些返回值在分配常规参数后仍然存在。

fmt.Printf() 的签名是:

func Printf(format string, a ...interface{}) (n int, err error)

除了函数调用(调用的返回值)之外,您不能将其他参数传递给fmt.Printf()

注意fmt.Println()的签名是:

func Println(a ...interface{}) (n int, err error)

这意味着fmt.Println(temp()) 可以工作,任何其他至少有一个返回值的函数也是如此,因为引用部分的最后一句话允许这样做(“如果f 有一个最终的@987654353 @ 参数,它被赋值为 g 的返回值,这些返回值在赋值常规参数后仍然存在。")

但通过一个小技巧,我们也可以通过fmt.Printf() 实现您想要的。

请注意,如果temp() 将返回[]interface{} 类型的值,我们可以使用... 将其作为某个可变参数的值传递。

意思是这样的:

func main() {
    fmt.Printf("1: %v, 2: %v\n", temp()...)
}

func temp() []interface{} { return []interface{}{1, 2} }

它可以正确打印(在Go Playground 上尝试):

1: 1, 2: 2

所以我们只需要一个实用函数,将任何函数的返回值包装到[]interface{} 中,这样我们就可以使用它来传递给fmt.Printf()

而且非常简单:

func wrap(vs ...interface{}) []interface{} {
    return vs
}

如上所述(使用fmt.Println()),我们可以将具有至少1 个返回值的任何函数的返回值作为其输入参数的值传递给wrap()

现在使用这个wrap()函数,看下面的例子:

func main() {
    fmt.Printf("1: %v\n", wrap(oneInt())...)
    fmt.Printf("1: %v, 2: %v\n", wrap(twoInts())...)
    fmt.Printf("1: %v, 2: %v, 3: %v\n", wrap(threeStrings())...)
}

func oneInt() int { return 1 }

func twoInts() (int, int) { return 1, 2 }

func threeStrings() (string, string, string) { return "1", "2", "3" }

这行得通,它会输出(在Go Playground 上试试):

1: 1
1: 1, 2: 2
1: 1, 2: 2, 3: 3

有关该主题的更多信息,请参阅相关问题:

Multiple values in single-value context

Return map like 'ok' in Golang on normal functions

【讨论】:

    【解决方案2】:

    不,这是不可能的。您必须将多值表达式中的所有值分配给单独的变量才能使用它们,例如:

    a, b := temp()
    fmt.Println("first = %d and second = %d", a, b)
    // first = 1 and second = 1
    

    [编辑]

    有趣的是,在某些情况下,如果参数类型和数量匹配,或者对于纯可变函数 (Go Playground),您可以使用多值表达式作为函数调用参数:

    func oneTwo() (int, int) {
      return 1, 2
    }
    
    func incr2(x, y int) (int, int) {
      return x + 1, y + 1
    }
    
    func main() {
      incr2(oneTwo()) // OK: multi-value return and arguments match.
    
      fmt.Println(oneTwo()) // OK: pure variadic function.
    
      fmt.Printf("%d %d", oneTwo()) // ERR: mixed formal and variadic args.
    }
    

    【讨论】:

    • 这被隐藏在问题 cmets 中,但对于处于更突出的视图中是有效的材料。这是一篇文章,详细说明了哪些是可能的,哪些是不可能的。 medium.com/golangspec/…
    【解决方案3】:

    当我没有使用任何字符串格式时,我能够打印它,如下所示

    fmt.Println(temp())
    

    只有Println支持,printf不支持

    【讨论】:

      猜你喜欢
      • 2023-01-21
      • 1970-01-01
      • 2015-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-10
      相关资源
      最近更新 更多