9

I want to use some external code that requires a pointer to a struct. At the point that the code is called, I have an interface variable.

When creating a pointer off of that variable, the pointer's type is interface{}* when I need it to be the pointer type of the struct's type.

Image the code in TestCanGetStructPointer does not know about the Cat class, and that it exists in some external package.

How can I cast it to this?

Here is a code sample:

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)
    }

}

The test fails with:

Pointer type was *interface {} but expected *parameterized.Cat

Oved D
  • 7,132
  • 10
  • 47
  • 69
  • 2
    `x := somethingForCats.getSomething().(Cat); &x` unless you change `return Cat{}` into `return &Cat{}` or `return new(Cat)` and then just `somethingForCats.getSomething().(*Cat)`. [Read the spec on type assertions](https://golang.org/ref/spec#Type_assertions). – Dave C Mar 23 '15 at 23:16
  • @DaveC eh, it can be played with a bit, but it's definitely not great for performance: http://stackoverflow.com/a/29222497/1162491 – Momer Mar 23 '15 at 23:50
  • @DaveC The place that this code is run will not know about the Cat type, so it cannot directly to that cast. Furthermore this code needs to work for any type getSomething() returns. It is for code in a separate package. You'd pass a pointer to the interface into this external package. – Oved D Mar 24 '15 at 03:49
  • @OvedD then edit your question. The first paragraph currently says you have an `interface{}` and need a `*structType` to pass somewhere else. A type assertion is the simplest way to do exactly that. – Dave C Mar 24 '15 at 18:39

3 Answers3

3

@dyoo's example does work, but it relies on you to manually cast Dog and Cat.

Here's a bit of a convoluted/verbose example which avoids that constraint somewhat:

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

Rick-777
  • 9,714
  • 5
  • 34
  • 50
Momer
  • 3,158
  • 22
  • 23
3

I found this thread: 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)
}
shawn
  • 4,305
  • 1
  • 17
  • 25
2

The following may help: 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)
    }
}
dyoo
  • 11,795
  • 1
  • 34
  • 44