为什么在Go语言中无法将[]string转换为[]interface{}?

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

why []string can not be converted to []interface{} in golang

问题

我发现很奇怪,为什么 []string 不能转换为 []interface{}?

我认为应该是可能的,因为:

  1. 它们都是切片
  2. []string 的每个元素都是字符串,而字符串当然是 interface{}

但在下面的示例中,它会导致编译错误:

func f(args ...interface{}){

}
s := []string{"ssd", "rtt"}
f(s...)

为什么语言不能自动完成转换呢?

英文:

I found its weird, why []string can not be converted to []interface{} ?

I think it should be possible, because:

  1. they are all slices
  2. every element of []string is string, which of course is interface{}

but in the example below, it will be a compilation error

func f(args ...interface{}){

}
s := []string{"ssd", "rtt"}
f(s...)

why the language can't finish the conversion automatically?

答案1

得分: 7

因为[]string[]interface{}在内存布局上有所不同。当你意识到interface{}变量需要知道它所包含的值的类型时,这一点就很明显。

对于[]string切片,支撑数组只需要保存各个字符串。而对于[]interface{}切片,你既有类型信息,又有字符串值(实际上是指向字符串值的指针,因为字符串的大小超过了一个内存字)。因此,从一种类型转换为另一种类型将涉及到数据的复制。

如果Go自动执行转换,会让代码的推理变得困难。例如,如果一个函数调用f(s)被声明为接受[]string参数,那么它可能会修改切片s中的字符串,但如果它被声明为接受[]interface{}参数,则不会修改。

英文:

Because []string and []interface{} have different in-memory layouts. This is obvious when you realise that an interface{} variable needs to know the type of the value it contains.

For a []string slice, the backing array only needs to hold the individual strings. For the []interface{} slice, you've got both type information and the string values (well, pointers to the string values, since a string is larger than a single word of memory). So converting from one type to the other will involve copying the data.

It would be confusing for Go to automatically perform a conversion, since it would make it difficult to reason about code. For example, a function call f(s) could modify the strings in the slice s if it was declared to take a []string argument, but not if it was declared to take a []interface{} argument.

答案2

得分: 7

切片基本上只是对底层数组的引用,包括起始指针、长度和容量。所以如果可能的话,考虑以下情况:

sliceOfStrings := []string{"one", "two", "three"}
// 输出 ONE TWO THREE
for i := range sliceOfStrings {
    fmt.Println(strings.ToUpper(sliceOfStrings[i]))
}

// 假设这是可能的
var sliceOfInterface = []interface{}(sliceOfStrings)
// 由于现在是 interface{} 数组,我们可以做任何操作
// 让我们把整数放在第一个位置
sliceOfInterface[0] = 1
// sliceOfStrings 仍然指向同一个数组,现在 "one" 被替换为 1
fmt.Println(strings.ToUpper(sliceOfStrings[0])) // BANG!

这个问题在Java和C#中也存在。实际上很少发生,但仍然存在。鉴于Go语言中没有像 int32 -> int64 这样的自动类型转换,如果你真的想将 []string 发送为 []interface{},强制你创建一个 []interface{} 的副本是有道理的。这样就不会有意外情况发生——你明确地写出来了,知道自己在做什么。如果函数修改了 []interface{},它不会对原始的 []string 产生影响。

英文:

Slice is basically just a reference to the underlying array, start pointer, length and capacity. So if it would be possible, then consider the following:

sliceOfStrings := []string{"one", "two", "three"}
// prints ONE TWO THREE
for i := range sliceOfStrings {
	fmt.Println(strings.ToUpper(sliceOfStrings[i]))
}

// imagine this is possible
var sliceOfInterface = []interface{}(sliceOfStrings)
// since it's array of interface{} now - we can do anything
// let's put integer into the first position
sliceOfInterface[0] = 1
// sliceOfStrings still points to the same array, and now "one" is replaced by 1
fmt.Println(strings.ToUpper(sliceOfStrings[0])) // BANG!

This issue exists in Java and C#. In practice it happens rarely, but still. Given that in Go there is no automatic type conversions like int32 -> int64 it makes sense that you are forced to create a []interface{} copy if you really want to send []string as []interface{}. This way there can be no surprise - you wrote it explicitly, you know what you're doing. And if function will modify []interface{} - it won't hurt original []string.

huangapple
  • 本文由 发表于 2014年1月24日 13:32:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/21325155.html
匿名

发表评论

匿名网友

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

确定