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

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)

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.
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.
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
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. If all else fails, you can use the reflect package to introspect your interface object and gather information about it. this is how the json package reads json text and returns populated structs—though this is an advanced topic and expect to sink a lot of time into it if you go this route. it’s best to hide reflection code inside a package with a clean interface(ie the package api).

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.

Related

Context value is nil when getting it with unexported struct key in Go HTTP handlers

Any help here is appreciated! I'm sure that I'm missing something really basic.
The problem I have is I am trying to get a value out of context in a demo web application, and I'm receiving the error:
2021/04/11 11:35:54 http: panic serving [::1]:60769: interface conversion: interface {} is nil, not []string
In my main function I'm setting the context with the following:
package main
type ctxKey struct{}
func someHttpHandleFunc() {
// .....
ctx := context.WithValue(r.Context, ctxKey{}, matches[1:])
route.handle(w, r.WithContext(ctx))
}
Then in my handler, I have the following:
package some_package
type ctxKey struct{}
func getField(r *http.Request, index int) string {
fields := r.Context().Value(ctxKey{}).([]string)
return fields[index]
}
I know that I'm missing something simple because if I try the above code and put my getField() function within the package main everything works.
For reference, this is a learning exercise, I'm trying to teach myself Go routing. I do know that there are routing packages available - but my goal is to learn. I'm trying my best to follow along with Different approaches to HTTP routing in Go. I have also read through Pitfalls of context values and how to avoid or mitigate them in Go. The latter seems to directly address the problem I'm having, but I can't seem to figure out how to solve it based on what is there.
Defined struct types defined in different packages are different.
package main
type ctxKey struct{}
is not the same type as
package some_package
type ctxKey struct{}
To make it intuitively clearer, think that if you were to reference these types from a third package — let's pretend the types were exported — you would have to import the corresponding package and use the appropriate selector:
package baz
import (
"myproject/foo"
"myproject/some_package"
)
func doBaz() {
foo := foo.CtxKey{}
bar := some_package.CtxKey{}
}
Now, the implementation of Value(key interface{}) uses the comparison operator == to determine whether the supplied key matches:
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
// then it checks if the key exists on the parent
return c.Context.Value(key)
}
Which implies that the types also must match. From the specs, comparison operators
In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.
And clearly in your example ctxKey struct{} declared in main is not assignable to ctxKey struct{} declared in some_package, and vice-versa, because their types differ.
To solve your error, make sure the keys used when setting and getting the context value are the same type. The best way, also to ensure proper encapsulation, would probably be to set and get the context value from the same package:
package some_ctx_helper_pkg
// unexported, to ensure encapsulation
type ctxKey struct{}
func Set(ctx context.Context, value interface{}) context.Context {
return context.WithValue(ctx, ctxKey{}, value)
}
func Get(ctx context.Context) interface{} {
return ctx.Value(ctxKey{})
}

usage of interface{} on a struct to check if it satisfies an interface in golang

Given the following code:
package main
import (
"fmt"
)
type work interface {
filter() bool
}
type organ struct {
name string
}
func (s *organ) filter () bool {
return true;
}
func main() {
kidney := &organ {
name : "kidney",
}
_, ok := interface{}(kidney).(work)
fmt.Println(ok);
}
I did not fully get the following part:
_, ok := interface{}(kidney).(work)
It seems to me, it is converting struct to the interface{} type, which I understand, but why is it required to convert to an interface{} type to check if it satisfies another interface. More specifically, why the following code fails?
ok := kidney.(work)
with error
invalid type assertion: kidney.(work) (non-interface type *organ on left)
TL;DR If you always know the concrete type (e.g., kidney), then you don't need a type assertion; just pass it into your work variable and carry on--the compiler will guarantee that kidney satisfies the work interface, otherwise your program won't compile.
The reason you must first convert the concrete type into an interface{} is because type assertions (i.e., dynamic type checks) only make sense between dynamic types (i.e., interfaces). It doesn't make sense to do a runtime type check on a thing the compiler can guarantee at compile time. Hopefully this makes sense?

Restore type information after passing through function as "interface {}"?

I'm running into a slight architectural problem with Golang right now that's causing me to copy/paste a bit more code than I'd prefer. I feel like there must be a solution, so please let me know if this is perhaps possible:
When I pass things through an interface {}-typed function parameter, I start getting errors such as "expected struct or slice", etc. ... even though what I passed was previously a struct or a slice. I realize that I could manually convert these to another type after receiving them in that function, but then that become tedious in instances such as this:
local interface type *interface {} can only be decoded from remote
interface type; received concrete type
... In this case, the receiving function seems like it'd need to be hard-coded to convert all interface {} items back to their respective original types in order to work properly, because the receiving function needs to know the exact type in order to process the item correctly.
Is there a way to dynamically re-type Golang interface {} typed variables back to their original type? Something like this, How to I convert reflect.New's return value back to the original type ... maybe?
EDIT: To clarify, basically, I'm passing &out to a function and it needs to be its original type by the time it reaches another inner function call.
Example code:
// NOTE: This is sort of pseudo-Golang code, not meant to be compiled or taken too seriously.
func PrepareTwoDifferentThings(keyA string, keyB string) {
var somethingA TypeA;
var somethingB TypeB;
loadFromCache(keyA, &somethingA, nil);
loadFromCache(keyB, &somethingB, nil);
fmt.Printf("Somethings: %v, %v", somethingA, somethingB);
}
func loadFromCache(key string, isNew, out interface {}, saveNewData interface {}) {
if err := cache.load(key, &out); err!=nil { // NOTE: Current issue is that this expects "&out" to be `TypeA`/`TypeB` not "interface {}", but I don't want to copy and paste this whole function's worth of code or whatever.
panic("oh no!");
}
if (saveNewData!=nil) {
cache.save(key, saveNewData); // This doesn't seem to care if "saveNewData" is "interface {}" when saving, but later cache fetches above using the "load()" method to an "interface {}"-typed `&out` parameter throw an exception that the "interface {}" type on `&out` does not match the original when it was saved here (`TypeA`/`TypeB`).
}
}
To change the type of an interface into its rightful type, you can use type assertions:
package main
import r "reflect"
type A struct {
Name string
}
func main() {
// No pointer
aa := A{"name"}
var ii interface{} = aa
bb := ii.(A)
// main.A
// Pointer
a := &A{"name"}
var i interface{} = a
b := *i.(*A)
// main.A
c := i.(*A)
// *main.A
d := r.Indirect(r.ValueOf(i)).Interface().(A)
// main.A
}
Playground 1
When using type assertions, you have to know the underlying type of your interface. In Go, there is no way to use type assertion with a dynamic type. reflect.Type is not a type, it's an interface representing a type. So no, you can't use it this way.
If you have several type possibilities, the solution is the type switch:
package main
import "fmt"
type TypeA struct {
A string
}
type TypeB struct {
B string
}
func doSomethingA(t TypeA) {
fmt.Println(t.A)
}
func doSomethingB(t TypeB) {
fmt.Println(t.B)
}
func doSomething(t interface{}) {
switch t := t.(type) {
case TypeA:
doSomethingA(t)
case TypeB:
doSomethingB(t)
default:
panic("Unrecognized type")
}
}
func main() {
a := TypeA{"I am A"}
b := TypeB{"I am B"}
doSomething(a)
// I am A
doSomething(b)
// I am B
}
Playground 2
It turns out that using JSON instead of Gob for serialization avoids the error that I was encountering entirely. Other functions can handle passing into interfaces, etc.

Is it necessary to use a type assertion to access the values of a type returned by interface?

When I have a function that returns an interface type, the returned value doesn't work as I would expect. That is, it acts strictly as the defined interface, and to access the methods and values not defined in the interface, I have to do a type assertion. Why?
Consider the following sample code:
package main
import (
"fmt"
)
type Frobnicator interface {
Frobnicate()
}
type Foo struct {
Value string
}
func (f *Foo) Frobnicate() {
fmt.Printf("The value is %s\n", f.Value)
}
func fooFactory () Frobnicator {
return &Foo{"chicken"}
}
func main() {
foo := fooFactory( )
foo.Frobnicate()
// foo.Value undefined (type Frobnicator has no field or method Value)
// fmt.Printf("foo value = %s\n", foo.Value)
bar := foo.(*Foo)
fmt.Printf("bar value = %s\n", bar.Value)
}
Is there a better, easier, more idiomatic way to get at foo.Value? Or is a type assertion really the best way?
Not sure what to answer here. Maybe there is a misconception what interface types are. Interface types are absolutely normal types. And you can do with a interface value what the interface says: Invoke the interface methods. For a struct type you may access fields and invoke interface methods as defined by the struct type. So everything is plain and simple: A type allows what it allows, no matter whether interface or struct.
It now happens that a value of interface type may contain some struct value (say). Up to now this is hidden. Type asserting reveals the struct value (and there is no more interface). You may hide an other struct value in the interface (given it implements the right methods) this might not have a Value field. This makes it clear that you cannot access the Value field without a type assertion, because it might not be there.
If you need access to inner value of interface implementation (Value), you must either expose it via interface itself or do a type assertion. That's because nothing in Frobnicator suggests whether it's Foo or some other implementing struct.
It is not different than many other languages. In Java you will have to cast also under similar circumstances.

Instance new Type (Golang)

Can anyone tell me how to create a new instance of Type from a string? Reflect?
There are examples but they are for the older (pre Go 1 versions) of the language [:(]
So, if I understand your question correctly, you are asking about how you can create an object when you just have the name of the type as string. So, for example, you might have a string "MyStruct" and you want to create an object of this type.
Unfortunately, that's not easily possible because Go is a statically typed language and the linker will eliminate dead code (or inline parts of it). So, there is no guarantee, that your final executable will even contain the code of "MyStruct".
You can however, maintain a global map[string]reflect.Type manually. For example by initializing this map in the init() function of your packages which defines such discover-able types. This will also tell the compiler that you are using the types. Afterwards, you can use this map to look up the reflect.Type of the type you want to create and use reflect.New to get a pointer to a new object of that type (stored as a reflect.Value). You can extract the object into an interface with something like this:
reflect.New(yourtype).Elem().Interface()
Elem() will de-reference the pointer and Interface() will return the reflected value as an interface{}. See The Laws of Reflection for further details.
PS: There might be a better way to structure your program which doesn't even require reflection and which let the compiler catch more errors. Have you considered using a factory method for example? An other easy solution might be to maintain a map[string]func() interface{} of functions which can be invoked to create a new object with that name.
Factory with predefined constructors can be based on something like:
package main
import (
"fmt"
)
type Creator func() interface{}
type A struct {
a int
}
type B struct {
a bool
}
func NewA() interface{} {
return new(A)
}
func NewB() interface{} {
return new(B)
}
func main() {
m := map[string]Creator{}
m["A"] = NewA
m["B"] = NewB
for k, v := range m {
fmt.Printf("%v -> %v\n", k, v())
}
}

Resources