英文:
why []string can not be converted to []interface{} in golang
问题
我发现很奇怪,为什么 []string 不能转换为 []interface{}?
我认为应该是可能的,因为:
- 它们都是切片
- []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:
- they are all slices
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论