How to cast interface{} back into its original struct?

删除回忆录丶 提交于 2020-01-22 16:29:06

问题


I need a way to dynamically cast a struct/interface back to its original object. I can add methods / functions inside. basically I need something like this:

MyStruct  =>  Interface{}  => MyStruct

When on the final conversion I don't know anything about the original struct besides what come inside the struct, so I can't just so:

a.(MyStruct)

回答1:


No: as mentioned in this thread

Go is neither covariant nor contravariant. Types are either equal or they aren't.

You have to either take the structs apart and deal with the pieces, or use reflection.
Type assertions are only "assertions", not "coercions" of any kind.

See also this thread, which reminds us that:

  • A pointer is one kind of type.
  • A struct is another kind of type.
  • An integer is another kind of type.
  • A floating point number is another kind of type.
  • A boolean is another kind of type.

The principle of an interface concerns the methods attached to a type T, not what type T is.

An interface type is defined by a set of methods.
Any value that implements the methods can be assigned to an interface value of that type.

That would make the conversion from interface to concrete type quite difficult to do.




回答2:


You need to know at least the possible types it could be. There's a couple cases, 1. You think you might know what it is. 2. You have a list of possible types it could be, 3. Your code knows nothing about the underlying types.

  1. If you think you know it, you can use type assertion to convert back to the original struct type.

...

package main

import (
    "fmt"
)

type MyStruct struct {
  Thing string
}

func (s *MyStruct) Display() {
  fmt.Println(s.Thing)
}

type Thingable interface {
  Display()
}

func main() {
  s := &MyStruct{
    Thing: "Hello",
  }

  // print as MyThing
  s.Display()

  var thinger Thingable
  thinger = s

  // print as thingable interface
  thinger.Display()

  // convert thinger back to MyStruct
  s2 := thinger.(*MyStruct) // this is "type assertion", you're asserting that thinger is a pointer to MyStruct. This will panic if thinger is not a *MyStruct

  s2.Display()
}

You can see this in action here: https://play.golang.org/p/rL12Lrpqsyu

Note if you want to test the type without panicking if you're wrong, do s2, ok := thinger.(*MyStruct). ok will be true if it was successful and false otherwise.

  1. if you want to test your interface variable against a bunch of types, use a switch: (scroll to bottom)

...

package main

import (
    "fmt"
    "reflect"
)

type MyStruct struct {
    Thing string
}

type MyStruct2 struct {
    Different string
}

func (s *MyStruct) Display() {
    fmt.Println(s.Thing)
}

func (s *MyStruct2) Display() {
    fmt.Println(s.Different)
}

type Thingable interface {
    Display()
}

func main() {
    s := &MyStruct{
        Thing: "Hello",
    }

    // print as MyThing
    s.Display()

    var thinger Thingable
    thinger = s

    // print as thingable interface
    thinger.Display()

    // try to identify thinger
    switch t := thinger.(type) {
    case *MyStruct:
        fmt.Println("thinger is a *MyStruct. Thing =", t.Thing)
    case *MyStruct2:
        fmt.Println("thinger is a *MyStruct2. Different =", t.Different)
    default:
        fmt.Println("thinger is an unknown type:", reflect.TypeOf(thinger))
    }
}

You can try that out here https://play.golang.org/p/7NEbwB5j6Is

  1. If you really don't know anything about the underlying types, you'll have to expose the things you need through interface functions and call those. Chances are you can do this without knowing anything about the underlying type.


来源:https://stackoverflow.com/questions/41660857/how-to-cast-interface-back-into-its-original-struct

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!