如何将多个返回值传递给可变参数函数?

8
我有一个返回两个整数值的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中做类似的事情吗?


1
OR,但不是两者都有。:=只是语法糖,编译器可以推断出分配的类型。 - RayfenWindspear
2
嗯,我不认为“多值表达式解构”与静态类型有任何关系。但我想对一个人来说显而易见的事情并不总是对另一个人显而易见,尤其是对于新手来说... - maerics
1
@RayfenWindspear 是的,看起来这是 Go 语言设计者的有意选择,而不是类型系统严格要求的强制要求。感谢您的跟进,我不知道那个例子会起作用! - maerics
1
@maerics 看起来这个函数可以接受可变参数 interface{},而且也适用于类型完全匹配的情况。今日所学。https://play.golang.org/p/Tbja3KNZO_W - RayfenWindspear
1
很让人烦恼的是,页面(例如在Chrome中的标签)名称在这里以python开头。标签可能被切换了。由于问题显然更适用于Go而不是Python,因此可能会引起混淆。 - h0ch5tr4355
显示剩余6条评论
3个回答

10

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


首先,为了实现您想要的功能,应该使用fmt.Printf()而不是fmt.Println(),因为只有前者期望并使用格式化字符串。

从现在开始,默认情况下不支持此操作,因为引用自规范:调用:

特殊情况下,如果函数或方法 g 的返回值数量相等且可以单独分配给另一个函数或方法 f 的参数,则调用 f(g(g的参数)) 将绑定 g 的返回值按顺序分配给 f 的参数以后调用 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有一个最终的...参数,则在分配常规参数之后,它将被赋予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

更多相关内容请参见:

在单值上下文中使用多个值

如何在Go中的普通函数中返回类似于map的“ok”值


6
不可以。您必须将多值表达式中的所有值分配给单独的变量才能使用它们,例如:
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.
}

1
这段内容埋在问题评论中,但适合放在更显眼的位置。以下是一篇详细介绍Go语言中多值表达式的可行性的文章链接:https://medium.com/golangspec/multi-valued-expressions-in-go-bd28ddfe1b39 - RayfenWindspear

0

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

fmt.Println(temp())

这只被Println支持,而不是printf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接