英文:
Copying struct value to a interface{ } in golang
问题
我将帮助你理解为什么在将结构体值复制到接口时会出现不同的行为。在这段代码中,有人可以帮助我理解为什么将mySlice的值复制到foo3时,它的行为与其他复制不同吗?
程序的输出结果是:
{824635465728 1 1}
{824635465728 1 1}
123456789
{824635465728 1 1}
{824635465728 1 1}
{4746976 824635330392 0}
编辑
我知道如果将foo3强制转换为:foo3.(SliceOfInt)
,它会起作用。但是为什么会这样呢?
英文:
I will like to understand why when I copy a struct value into an interface it behaves like it does. In this code can someone help me understand why when I copy the value from mySlice into foo3 it behaves different than the other copies?
package main
import (
"fmt"
"unsafe"
)
type SliceOfInt []int
// when passing a slice to a method you are passing this data. Lets prove it
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
// Print the header of a slice. Its Data. Len and Cap
func GetHeaderOfSliceOfInt(s unsafe.Pointer) *SliceHeader {
// this header needs to point to &mySlice but compiler will not let us. we have to use unsafe pointers
var header *SliceHeader
pointerToMySlice := s
header = ((*SliceHeader)(pointerToMySlice))
return header
}
func main() {
// On go everything is passed by coping values. When you pass a slice to a function you are passing this:
// reference: https://stackoverflow.com/a/39993797/637142
/*
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
*/
// create a new slice
var mySlice SliceOfInt = make([]int, 0)
mySlice = append(mySlice, 123456789) // append this number to mySlice
// now we have a slice with len:1 and capacity:1. Lets prove it
header := GetHeaderOfSliceOfInt(unsafe.Pointer(&mySlice))
fmt.Println(*header)
// this prints: {824635465728 1 1}
// this means that on memory address 824635465728 there is an array with cap:1 and len:1
// copy that header to someOtherSlice
someOtherSlice := mySlice
header = GetHeaderOfSliceOfInt(unsafe.Pointer(&someOtherSlice))
fmt.Println(*header)
// prints the same value {824635465728 1 1}
// anyways if I go to address 824635465728 on my computer I shoul dbe able to find integer 123456789
pointerToInteger := unsafe.Pointer((*header).Data)
var integerVal *int = ((*int)(pointerToInteger))
fmt.Println(*integerVal)
// if I copy like this, it will print the correct header {824635465728 1 1}
foo1 := mySlice
fmt.Println(*GetHeaderOfSliceOfInt(unsafe.Pointer(&foo1)))
// copy like this will also print the correct header {824635465728 1 1}
foo2 := foo1
fmt.Println(*GetHeaderOfSliceOfInt(unsafe.Pointer(&foo2)))
// If I copy like this it will print the incorrect header. Why?
var foo3 interface{} = mySlice
fmt.Println(*GetHeaderOfSliceOfInt(unsafe.Pointer(&foo3)))
// this last line prints {4746976 824635330392 0}
}
The output of the program is:
{824635465728 1 1}
{824635465728 1 1}
123456789
{824635465728 1 1}
{824635465728 1 1}
{4746976 824635330392 0}
Edit
I know that if I cast foo3 as: foo3.(SliceOfInt)
it will work. But why is that?
答案1
得分: 2
接口类型,无论是否为空,都是一种独立的类型。它有自己的内存表示,并且是Go类型系统中合法的成员。
接口值和包装在接口中的值并不相同。
变量foo1
和foo2
与mySlice
变量具有相同的类型和值。但是变量foo3
具有不同的类型,因此也具有不同的值。是的,动态类型和值与mySlice
相同,但静态类型和值不同。
接口值在内存中不是由与三字段SliceHeader
兼容的结构表示的,因此尝试从接口值中取出切片头是错误的,不仅在语义上是错误的。相反,接口值由一个2字段结构表示(这就是为什么在你的尝试中第三个字段是0
)。第一个字段指向包装值的类型信息,第二个字段指向包装值的数据。
类似这样:
type iface struct {
typ uintptr
data uintptr
}
你可以通过以下方式进行测试:
x := (*iface)(unsafe.Pointer(&foo3))
s := (*SliceHeader)(unsafe.Pointer(x.data))
fmt.Printf("%+v\n", x)
fmt.Printf("%+v\n", s)
https://go.dev/play/p/2KUgCU8h7O7
另外,考虑阅读这篇文章:https://research.swtch.com/interfaces。
英文:
An interface type, empty or not, is a type in its own right. It has its own memory representation and it is a legitimate member of Go's type system.
An interface value, and the value wrapped in that interface, are not one and the same.
The variables foo1
and foo2
have the same type and value as the mySlice
variable. But the variable foo3
has a different type, therefore also a different value. And yes, the dynamic type and value are the same as mySlice
but the static type and value are not.
An interface value is NOT represented in memory by a structure that's compatible with the three-field SliceHeader
and therefore it is wrong, not only semantically, to try to take the slice header off of an interface value. Instead, an interface value is represented by a 2-field structure (that's why in your attempt the third field is 0
). The first field points to the type information of the wrapped value, the second field points to the data of the wrapped value.
Something like this:
type iface struct {
typ uintptr
data uintptr
}
And you can test this by doing this:
x := (*iface)(unsafe.Pointer(&foo3))
s := (*SliceHeader)(unsafe.Pointer(x.data))
fmt.Printf("%+v\n", x)
fmt.Printf("%+v\n", s)
https://go.dev/play/p/2KUgCU8h7O7
Also, consider reading this: https://research.swtch.com/interfaces.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论