繁体   English   中英

将结构值复制到 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: 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}
}

程序的output为:

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

编辑

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

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

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

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

接口值在 memory 中未由与三字段SliceHeader兼容的结构表示,因此尝试从接口值中取出切片 header 不仅在语义上是错误的。 相反,接口值由 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM