在Go语言中,如何将接口指针转换为结构体指针?

10
我希望使用一些需要指向结构体的指针的外部代码。在调用代码的时候,我有一个接口变量。
当从该变量创建指针时,指针的类型是 interface{}*,而我需要它是结构体类型的指针类型。
假设代码中的 TestCanGetStructPointer 不知道 Cat 类 的存在,并且它存在于某个外部程序包中。
我该如何将其强制转换为这个类型?
以下是代码示例:
import (
    "reflect"
    "testing"
)   

type Cat struct {
    name string
}

type SomethingGeneric struct {
    getSomething func() interface{}
}

func getSomeCat() interface{} {
    return Cat{}
}

var somethingForCats = SomethingGeneric{getSomething: getSomeCat}

func TestCanGetStructPointer(t *testing.T) {
    interfaceVariable := somethingForCats.getSomething()

    pointer := &interfaceVariable

    interfaceVarType := reflect.TypeOf(interfaceVariable)
    structPointerType := reflect.PtrTo(interfaceVarType)
    pointerType := reflect.TypeOf(pointer)

    if pointerType != structPointerType {
        t.Errorf("Pointer type was %v but expected %v", pointerType, structPointerType)
    }

}

测试失败,出现以下错误:
指针类型为*interface {},但预期为*parameterized.Cat

2
x := somethingForCats.getSomething().(Cat); &x,除非您将 return Cat{} 更改为 return &Cat{}return new(Cat),否则只需使用 somethingForCats.getSomething().(*Cat)请阅读类型断言规范 - Dave C
@DaveC 嗯,可以稍微调整一下,但性能肯定不是很好:https://dev59.com/XInca4cB1Zd3GeqP4wPr#29222497 - Momer
@DaveC 这段代码运行的地方不知道 Cat 类型,因此无法直接进行转换。此外,这段代码需要适用于 getSomething() 返回的任何类型。它是用于一个单独包中的代码。您需要将接口指针传递到这个外部包中。 - Oved D
@OvedD 然后编辑你的问题。第一段目前说你有一个 interface{} 并需要一个 *structType 传递到其他地方。类型断言是实现这一点的最简单方法。 - Dave C
3个回答

3
@dyoo的例子可以工作,但需要您手动转换DogCat类型。

下面是一个有点复杂/冗长的例子,可以避免这种限制:

package main

import (
    "fmt"
    "reflect"
)

type Cat struct {
    name string
}

type SomethingGeneric struct {
    getSomething func() interface{}
}

func getSomeCat() interface{} {
    return Cat{name: "Fuzzy Wuzzy"}
}

var somethingForCats = SomethingGeneric{getSomething: getSomeCat}

func main() {
    interfaceVariable := somethingForCats.getSomething()
    castVar := reflect.ValueOf(interfaceVariable)
    castVar.Convert(castVar.Type())

    // If you want a pointer, do this:
    fmt.Println(reflect.PtrTo(castVar.Type()))

    // The deref'd val
    if castVar.Type() != reflect.TypeOf(Cat{}) {
        fmt.Printf("Type was %v but expected %v\n", castVar, reflect.TypeOf(&Cat{}))
    } else {
        fmt.Println(castVar.Field(0))
    }
}

Playground Link


3

我发现了这个帖子:https://groups.google.com/forum/#!topic/golang-nuts/KB3_Yj3Ny4c

(此内容为原文,无需翻译)
package main

import (
    "fmt"
    "reflect"
)

type Cat struct {
    name string
}

//
// Return a pointer to the supplied struct via interface{}
//
func to_struct_ptr(obj interface{}) interface{} {

    fmt.Println("obj is a", reflect.TypeOf(obj).Name())

    // Create a new instance of the underlying type 
    vp := reflect.New(reflect.TypeOf(obj))

    // Should be a *Cat and Cat respectively
    fmt.Println("vp is", vp.Type(), " to a ", vp.Elem().Type())

    vp.Elem().Set(reflect.ValueOf(obj))

    // NOTE: `vp.Elem().Set(reflect.ValueOf(&obj).Elem())` does not work

    // Return a `Cat` pointer to obj -- i.e. &obj.(*Cat)
    return vp.Interface()
}

//
// Dump out a pointer ...
//
func test_ptr(ptr interface{}) {
    v := reflect.ValueOf(ptr)
    fmt.Println("ptr is a", v.Type(), "to a", reflect.Indirect(v).Type())
}

func main() {
    cat := Cat{name: "Fuzzy Wuzzy"}

    // Reports "*main.Cat"
    test_ptr(&cat)

    // Get a "*Cat" generically via interface{}
    sp := to_struct_ptr(cat)

    // *should* report "*main.Cat" also
    test_ptr(sp)

    fmt.Println("sp is",sp)
}

2
以下内容可能有所帮助:http://play.golang.org/p/XkdzeizPpP
package main

import (
    "fmt"
)

type Cat struct {
    name string
}

type Dog struct {
    name string
}

type SomethingGeneric struct {
    getSomething func() interface{}
}

func getSomeCat() interface{} {
    return Cat{name: "garfield"}
}

func getSomeDog() interface{} {
    return Dog{name: "fido"}
}

var somethings = []SomethingGeneric{
    SomethingGeneric{getSomething: getSomeCat},
    SomethingGeneric{getSomething: getSomeDog},
}

func main() {
    for _, something := range somethings {
        interfaceVariable := something.getSomething()

        cat, isCat := interfaceVariable.(Cat)
        dog, isDog := interfaceVariable.(Dog)

        fmt.Printf("cat %v %v\n", cat, isCat)
        fmt.Printf("dog %v %v\n", dog, isDog)
    }
}

运行此代码的程序无法访问Cat或Dog类。 - Oved D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接