将结构值复制到 golang 中的接口{}

Copying struct value to a interface{ } in golang

我想了解为什么当我将一个结构值复制到一个接口中时它的行为就像它那样。在此代码中,有人可以帮助我理解为什么当我将值从 mySlice 复制到 foo3 时,它的行为与其他副本不同吗?

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: 
    /*
        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}
}

程序的输出是:

{824635465728 1 1}
{824635465728 1 1}
123456789
{824635465728 1 1}
{824635465728 1 1}
{4746976 824635330392 0}

编辑

我知道如果我将 foo3 转换为:foo3.(SliceOfInt) 它会起作用。但这是为什么呢?

接口类型,无论是否为空,本身就是一种类型。它有自己的内存表示,是 Go 类型系统的合法成员。

接口值和包装在该接口中的值不是一回事。

变量 foo1foo2mySlice 变量具有相同的类型和值。但是变量 foo3 具有不同的类型,因此也具有不同的值。是的,dynamic 类型和值与 mySlice 相同,但 static 类型和值不同。

接口值在内存中不是由与 three-field 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