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

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?

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

Type assertion to an embedded type from interface{}

I want to use type assertion from interface{} to a type I sure the content of the interface{} has embedded reference of. https://play.golang.org/p/G8YrS7WZyQU
package main
import "fmt"
type S1 struct {}
type S2 struct {*S1}
func main() {
s := interface{}(&S2{&S1{}})
// I do not know the s type, but know that it is of `struct {*S1}`
_, ok := s.(*S1)
fmt.Println(ok)
}
Is it even possible in Go?
No, you can't do this. The spec says that you can only type-assert an interface to a type that is identical to the type of the value that was stored in the interface. It also says "A defined type is always different from any other type". Structurally compatible isn't good enough; the only type you can use in your assertion is S2.
Reflection would be possible (see that the value has a Kind of Struct, and has a Field which is a Pointer which has an Elem with a Type that equals reflect.TypeOf(S1{})), at which point an assertion to S1 would succeed on that elem's Interface (but not an assertion to the embedding type).

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

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.

Golang type conversion/assertion issue with unmarshalling json

package main
import (
"fmt"
"encoding/json"
"reflect"
)
type GeneralConfig map[string]interface{}
var data string = `
{
"key":"value",
"important_key":
{"foo":"bar"}
}`
func main() {
jsonData := &GeneralConfig{}
json.Unmarshal([]byte(data), jsonData)
fmt.Println(reflect.TypeOf(jsonData)) //main.GeneralConfig
jsonTemp := (*jsonData)["important_key"]
fmt.Println(reflect.TypeOf(jsonTemp)) //map[string]interface {}
//newGeneralConfig := GeneralConfig(jsonTemp)
//cannot convert jsonTemp (type interface {}) to type GeneralConfig:
//need type assertion
newGeneralConfig := jsonTemp.(GeneralConfig)
//fmt.Println(reflect.TypeOf(newGeneralConfig))
//panic: interface conversion: interface {} is map[string]interface {},
//not main.GeneralConfig
}
Available at the playground
I understand that I can use a nested struct in lieu of GeneralConfig, but that would require me knowing the exact structure of the payload, ie it wouldn't work for different keys (I would be locked into "important_key").
Is there a golang workaround for when I don't know what the value of "important_key" is? I say golang, because if possible, one could require all "important_keys" to have a constant parent key, which could resolve this issue.
To summarize, given an arbitrary json object, there must be a way that I can traverse its keys, and if a value is a custom type, convert the value to that type. Right now it seems that if I use type conversion, it tells me that the type is interface{} and I need to use type assertion; however, if I use type assertion, it tells me that interface{} is map[string]interface{} not main.GeneralConfig.
I agree the comments about trying to utilise the expected structure of the incoming JSON in order to write well-defined Structs, but I'll attempt to answer the question anyway.
The thing to take away from what you're seeing printed versus the error messages that you're seeing is that the compiler knows less about the type than the runtime because the runtime can look at the actual value. To bring the compiler up-to-speed we must (i) assert (*jsonData)["important_key"] is a map[string]interface{} -- the compiler only knows it to be an interface{} -- and then (ii) type-cast that to a GeneralConfig type. See:
package main
import (
"fmt"
"encoding/json"
)
type GeneralConfig map[string]interface{}
func main() {
jsonStruct := new(GeneralConfig)
json.Unmarshal([]byte(`{"parent_key": {"foo": "bar"}}`), jsonStruct)
fmt.Printf("%#v\n", jsonStruct)
// => &main.GeneralConfig{"parent_key":map[string]interface {}{"foo":"bar"}}
nestedStruct := (*jsonStruct)["parent_key"]
fmt.Printf("%#v\n", nestedStruct)
// => map[string]interface {}{"foo":"bar"}
// Whilst this shows the runtime knows its actual type is
// map[string]interface, the compiler only knows it to be an interface{}.
// First we assert for the compiler that it is indeed a
// map[string]interface{} we are working with. You can imagine the issues
// that might arrise if we has passed in `{"parent_key": 123}`.
mapConfig, ok := nestedStruct.(map[string]interface{})
if !ok {
// TODO: Error-handling.
}
// Now that the compiler can be sure mapConfig is a map[string]interface{}
// we can type-cast it to GeneralConfig:
config := GeneralConfig(mapConfig)
fmt.Printf("%#v\n", config)
// => main.GeneralConfig{"foo":"bar"}
}
You are looking for json.RawMessage.
You can delay unmarshalling based upon some other value and then force it to unmarshal to a specific type.
This is not a good idea, but might be closer to what you are looking for.
http://play.golang.org/p/PWwAUDySE0
This is a standard "workaround" if get what you're after. When handling unknown data you can implement this pattern (modified from your example) of switching on the type recursively to get to the concrete values in an unknown body of json data.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
var data = `
{
"key":"value",
"important_key":
{"foo":"bar"}
}`
func main() {
var jsonData interface{}
json.Unmarshal([]byte(data), &jsonData)
fmt.Println(reflect.TypeOf(jsonData))
parseArbitraryJSON(jsonData.(map[string]interface{}))
}
func parseArbitraryJSON(data map[string]interface{}) {
for k, v := range data {
switch a := v.(type) {
case string:
fmt.Printf("%v:%v\n", k, a)
case map[string]interface{}:
fmt.Printf("%v:%v\n", k, a)
parseArbitraryJSON(a)
}
}
}
The resulting output is:
map[string]interface {}
key:value
important_key:map[foo:bar]
foo:bar
This example only accounts for the base data being a string type but you can switch on any type that you expect to receive, and like any switch you can group your cases, so you can treat all numbers similarly for example.

In golang, how to type assert an interface{} to a type specified by a reflect.Type?

For example, I have an interface{} named a, and I also have an reflect.Type called elemType. Now, I want to type assert a to elemType, but a.(elemType) can't be compiled successfully. How to fix it?
Sorry for my confusing expression. My meaning is that I get a type from a function, and I want to type assert an interface{} to this type, but this type is stored in a reflect.Type variable.
What I want to do is similar to the code below:
var a interface{}
//do something
func getType() reflect.Type {
var ret reflect.Type
//do something
return ret
}
targetType := getType()
result := a.(targetType)
Consider a standard type assertion in Go:
v := a.(typeName)
Here the compiler can determine the type of the variable v at compile time, and make use of that knowledge when compiling any further statements involving the variable.
With your example of using a refltect.Type variable in the assertion, it would be impossible to determine the type of v, so the code could not be compiled.
If you need to check that a particular interface variable is of a particular type at runtime, you can still do that with the reflect package. For example:
// if elemType is a normal type
if reflect.ValueOf(a).Type() == elemType {
fmt.Println("type matches")
}
// if elemType is an interface, can check if the value implements it
if reflect.ValueOf(a).Type().Implements(elemType) {
fmt.Println("value implements interface")
}
But you will need a concrete type to return back to standard variables. If you've only got a small selection of possible types, perhaps using a type switch might do what you want.

Resources