将 []interface{} 转换为非变参函数的参数

huangapple go评论76阅读模式
英文:

Turning []interface{} into arguments to a non-variadic function

问题

我正在寻找一种优雅的方法来解压缩 Go 中的参数列表。我不想使用可变参数函数,因为在编写函数时,我已经知道参数的数量,并且希望保持代码简洁。然而,在我的用例中,参数以 []interface{} 的形式传入。

我找不到解决方案,但也许有人已经知道如何做到这一点呢?

package main

import (
	"fmt"
)

// 非可变参数的 greet 函数
func greet(n1, n2 string) {
	fmt.Printf("%s %s\n", n1, n2)
}

func main() {
	l := []interface{}{"hello", "world"}

	// 这样可以工作
	greet(l[0].(string), l[1].(string))

	// 这样不行:"./args.go:20: not enough arguments in call to greet"
	//greet(l...)

	// 有没有更优雅的方法来解压缩列表?
}
英文:

I am looking for an elegant way to unzip a list of arguments in Go. I do not want to use a variadic function for that purpose because in my usecase when writing a function I already know the number of arguments and I want to keep that part simple. However in my usecase the parameters arrive as []interface{}.
I could not find a solution but hey maybe someone out there already knows how to do that?

package main

import (
	"fmt"
)

// NON-VARIADIC greater
func greet(n1, n2 string) {
	fmt.Printf("%s %s\n", n1, n2)
}


func main() {
	l := []interface{}{"hello", "world"}

	// works
	greet(l[0].(string), l[1].(string))

	// does not work: "./args.go:20: not enough arguments in call to greet"
	//greet(l...)

	// is there something more elegant to unzip the list?
}

答案1

得分: 3

你可以使用 reflect 包创建一个“通用”调用器,尽管这会增加开销并且缺乏类型安全性。除非你有特殊情况并且不知道在代码中要调用什么,否则最好使用你问题中的代码片段,虽然你认为它不够优雅。

以下是使用 reflect 的示例用法,可以作为你的起点:

package main

import (
	"fmt"
	"reflect"
)

func call(f interface{}, args []interface{}) {
	// 将参数转换为 reflect.Value
	vs := make([]reflect.Value, len(args))
	for n := range args {
		vs[n] = reflect.ValueOf(args[n])
	}
	// 调用函数。注意,如果 f 不可调用或参数不匹配,会引发 panic
	reflect.ValueOf(f).Call(vs)
}

func greet(n1, n2 string) {
	fmt.Printf("%s %s\n", n1, n2)
}

func main() {
	l := []interface{}{"hello", "world"}
	call(greet, l)
}

// 输出:hello world

链接:https://play.golang.org/p/vbi3CChCdV

英文:

You could create a "generic" caller using reflect package, although this comes with overhead and lacks type safety. Unless you have some special case situation and don't know what you want to call in the code, it would be wiser to use the snippet from your question which works, but you consider not elegant.

Example usage of reflect which could be your starting point:

package main

import (
	"fmt"
	"reflect"
)

func call(f interface{}, args []interface{}) {
	// Convert arguments to reflect.Value
	vs := make([]reflect.Value, len(args))
	for n := range args {
		vs[n] = reflect.ValueOf(args[n])
	}
	// Call it. Note it panics if f is not callable or arguments don't match
	reflect.ValueOf(f).Call(vs)
}

func greet(n1, n2 string) {
	fmt.Printf("%s %s\n", n1, n2)
}

func main() {
	l := []interface{}{"hello", "world"}
	call(greet, l)
}

// Output: hello world

https://play.golang.org/p/vbi3CChCdV

答案2

得分: 1

我不太确定你想做什么。如果你想要一种简便的方法将两个空接口的切片传递给一个接受两个字符串的函数,你可以创建一个小的辅助函数:

func twoStrings(vs []interface{}) (string, string) {
    return vs[0].(string), vs[1].(string)
}

使用方法如下:

greet(twoStrings(l))

Playground: http://play.golang.org/p/R8KFwMUT_V.

但是老实说,看起来你正在做一些错误的事情,试图让Go类型系统做一些它不能做的事情。

英文:

I'm not quite sure what you're trying to do. If you want a way to easily pass a slice of two empty interfaces to a function that accepts two strings, you can create a little helper:

func twoStrings(vs []interface{}) (string, string) {
    return vs[0].(string), vs[1].(string)
}

Use it as

greet(twoStrings(l))

Playground: http://play.golang.org/p/R8KFwMUT_V.

But honestly, it seems like you're doing something wrong, trying to make the Go type system to do something it cannot do.

huangapple
  • 本文由 发表于 2015年12月11日 00:37:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/34207143.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定