将结构体值复制到golang中的interface{}中

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

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类型系统中合法的成员。

接口值和包装在接口中的值并不相同。

变量foo1foo2mySlice变量具有相同的类型和值。但是变量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.

huangapple
  • 本文由 发表于 2022年4月16日 09:13:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/71890303.html
匿名

发表评论

匿名网友

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

确定