关于Go反射:在Go中转换接口切片的类型

Type converting slices of interfaces in Go

我很好奇为什么Go没有隐式地将[]T转换为[]interface{},而它将隐式地将T转换为interface{}。关于这个转换,我是否遗漏了一些不平凡的东西?

例子:

1
2
3
4
5
6
func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello","world"}
    foo(a)
}

go build号投诉

cannot use a (type []string) as type []interface {} in function argument

如果我想明确地做,同样的事情:b := []interface{}(a)抱怨

cannot convert a (type []string) to type []interface {}

所以每次我需要做这个转换(这看起来是很多),我都在做这样的事情:

1
2
3
4
b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

有没有更好的方法可以做到这一点,或者标准库函数可以帮助进行这些转换?每次我想调用一个可以获取一个列表的函数(如int或strings),再多写4行代码似乎有点傻。


在go中,有一个一般规则,语法不应隐藏复杂/昂贵的操作。将string转换为interface{}是在o(1)时间内完成的。将[]string转换为interface{}也在o(1)时间内完成,因为切片仍然是一个值。但是,将[]string转换为[]interface{}是O(n)时间,因为切片的每个元素都必须转换为interface{}

这个规则的一个例外是转换字符串。在将string转换为[]byte[]rune和从[]byte[]rune转换时,go不起作用,即使转换是"语法"。

没有标准的库函数可以为您进行这种转换。您可以使用reflect创建一个,但它将比三行选项慢。

反射示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func InterfaceSlice(slice interface{}) []interface{} {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    ret := make([]interface{}, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = s.Index(i).Interface()
    }

    return ret
}

不过,您最好的选择是使用您在问题中给出的代码行:

1
2
3
4
b := make([]interface{}, len(a))
for i := range a {
    b[i] = a[i]
}


您缺少的是拥有T值的Tinterface{},它们在内存中具有不同的表示,因此不能进行一般的转换。

T类型的变量只是它在内存中的值。没有关联的类型信息(在go中,每个变量都有一个在编译时而不是运行时已知的类型)。它在内存中的表示方式如下:

  • 价值

拥有T类型变量的interface{}在内存中表示如下

  • 指向类型T的指针
  • 价值

所以回到你原来的问题:为什么不隐含地把[]T转换成[]interface{}

[]T转换为[]interface{}需要创建一个新的interface {}值切片,这是一个非常重要的操作,因为内存布局完全不同。


官方解释如下:https://github.com/golang/go/wiki/interfaceslice

1
2
3
4
5
var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
    interfaceSlice[i] = d
}

试试interface{}。要将其转换为切片,请尝试

1
2
3
4
func foo(bar interface{}) {
    s := bar.([]string)
    // ...
}


将接口转换为任何类型

Syntax:

1
result := interface.(datatype)

例子:

1
2
var employee = interface{["Jhon","Arya"]}
result := employee.([]string)   //result have type of []string.