How to handle missing variable panic properly? - go

I have the following simple go-lang code.
aud := claims["aud"].(string)
It's a jwt claim so it always exist. Then I was trying to experiment on changing the variable from aud to missing-aud.
When I changed it, then tested. I noticed a critical panic.
[PANIC RECOVER] interface conversion: interface {} is nil, not string goroutine 7
What is the simplest way to handle a missing variable and not panic?
I have tried, but it seems not the case.
var aud string
if claims["audx"].(string) != "" {
aud = claims["audx"].(string)
}
Thanks for anyone who could help
Modified with an answer, but was looking if this could be simplified?
var aud string
if _, found := claims["audx"].(string); found {
aud = claims["audx"].(string)
}

As #JimB pointed out - you are confusing type assertion (claims["audx"].(string)) with a map-key existence check (v, ok := claims["audx"]):
If your map value is of type interface{} - then yes you need type assertion. But you also want a key check before you check the value e.g.
if v, ok := claims["audx"]; ok {
log.Printf("audx key exists")
if vs, ok := v.(string); ok {
// "audx" value is a string
aud = vs
log.Printf("audx value is a string: %q", aud)
}
}
This playground example should make it clearer: https://play.golang.org/p/pTOR39wDCx5

The form of your if statement is valid, and would work here, but the found value is not what you are expecting. It is the result of the type assertion, rather than the map index.
_, found := claims["audx"].(string)
can be broken down as
v := claims["audx"]
_, ok := v.(string)
Since the zero value for the type assertion is valid for your purpose, and you are taking no action on the result of the index or assertion conditionals, you can simplify the expression to:
audx, _ := claims["audx"].(string)
This works because if the map returns nil, the type assertion simply fails, but will give you the zero value of that type.

Related

Golang manipulating object inside a sync.Map

I am trying to manipulate a golang sync.Map of sync.Map, but I have some issues with the casting.
I have the following code:
func (cluster *Cluster) action(object1, object2 MyObject) {
value, _ := cluster.globalMap.LoadOrStore(object1.name, sync.Map{})
localMap := value.(sync.Map)
localMap.Store(object2.Name, object2)
value2, _ := cluster.resourceInflight.Load(node.Name)
forComparison := value2.(sync.Map)
fmt.Println(localMap.Load(object2.Name))
fmt.Println(forComparison.Load(object2.Name))
}
{myObject map[] map[]} true
<nil> false
I am doing this since I wish to keep the content of localMap thread safe.
The problem is I am expecting to have the same result for my two print, as "forComparison" should be pointing to the same object than "localMap". But second result is nil.
I am suspecting that the problem is coming from the casting of the interface "value" into an actual "sync.Map". But I am not sure how I can actually call the .Store method with inline casting.
I thought about Storing localMap inside cluster.globalMap, but this seems incorrect to me as it would break the whole point of using a localSyncMap and create concurrency issues.
Any input on what I should do ?
As per the comments the issue was that you were copying a sync.Map; the following code will fail (output "Not found" - playground):
var sm sync.Map
var x interface{}
x = sm
sm2 := x.(sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
fmt.Printf("Found: %s\n", result)
} else {
fmt.Printf("Not found\n")
}
Whereas using a pointer works as expected:
var sm sync.Map
var x interface{}
x = &sm
sm2 := x.(*sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
fmt.Printf("Found: %s\n", result)
} else {
fmt.Printf("Not found\n")
}
Running go vet would probably have warned you about other issues (sync.Map contains a sync.Mutex and these "must not be copied after first use").
Note that the docs for Sync.Map state:
The Map type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.

assert interface{} to int64

i am using gin, i use c.shouldBind to bind the data to the struct, the use c.set to set the params in the context. when i use c.getInt64 to get the params, it can't return the value i set in the context(i set a 1), it return a zero. it failed to assert a float64 1 to a int64 0.
i have google it but can't get the answer i want
here are my debug code
// GetInt64 returns the value associated with the key as an integer.
func (c *Context) GetInt64(key string) (i64 int64) {
if val, ok := c.Get(key); ok && val != nil {
i64, _ = val.(int64)
}
return
}
the val is 1, but it returns 0
So can anybody tell me why and how to solve it.
Golang can't convert types implicitly. You can use the GetFloat64 method.
It depends the value which you really set in, and check the key is right, After that, Maybe you could use assertion after Get like: value := c.Get("From_validator_page").(Int64)

How can I assign a reflect.Value that holds an error object to an ordinary variable of type error?

I am trying to write a function that returns back to its caller an error result taken from the result slice of reflect.ValueOf(somefunc).Call(someargs).
I've tried numerous variants of refer. calls, and type assertions. but cannot seem to get the compiler to let me put the actual concrete value from the reflect.Value slice back into an ordinary error variable.
Here's the code, using os.Getwd as the function:
var somefunc interface{}
var errToCaller *error
somefunc = os.Getwd
ftype := reflect.TypeOf(somefunc)
errType := reflect.TypeOf(errToCaller).Elem()
resType := ftype.Out(1)
fmt.Println("resType.Implements(errType) = ",
resType.Implements(errType))
res := reflect.ValueOf(somefunc).Call([]reflect.Value{})
fmt.Printf("res[1] as %%v = %v\n", res[1])
fmt.Printf("res[1] as %%#v = %#v\n", res[1])
fmt.Printf("ValueOf(res[1]) as %%v = %v\n",
reflect.ValueOf(res[1]))
fmt.Printf("ValueOf(res[1]) as %%#v = %#v\n",
reflect.ValueOf(res[1]))
fmt.Printf("ValueOf(res[1]).Type() as %%#v = %#v\n",
reflect.ValueOf(res[1]).Type())
fmt.Printf("ValueOf(res[1]).Interface() as %%#v = %#v\n",
reflect.ValueOf(res[1]).Interface())
// *errToCaller = reflect.ValueOf(res[1])
// *errToCaller = reflect.ValueOf(res[1]).Interface()
// *errToCaller = reflect.ValueOf(res[1]).Interface().(error)
With the following output:
resType.Implements(errType) = true
res[1] as %v = <nil>
res[1] as %#v = error(nil)
ValueOf(res[1]) as %v = <error Value>
ValueOf(res[1]) as %#v = reflect.Value{typ:(*reflect.rtype)(0x4b9a60), ptr:(unsafe.Pointer)(0xc42000a3f0), flag:0x94}
ValueOf(res[1]).Type() as %#v = &reflect.rtype{size:0x18, ptrdata:0x10, hash:0x500c1abc, tflag:0x7, align:0x8, fieldAlign:0x8, kind:0x19, alg:(*reflect.typeAlg)(0x4a62d0), gcdata:(*uint8)(0x4daa41), str:21371, ptrToThis:184032}
ValueOf(res[1]).Interface() as %#v = error(nil)
I've abbreviated the example to remove lots of other Printf statements that indicate (to me at least) that the types are the same (even in what I think are the relevant fields of the reflect.Value struct). Why, when all of the various print statements seem to be telling me the result is an error value, can't I assign it to my local variable?
Uncommenting the first assignment in the code example above results in this complaint from the compiler:
./passerror.go:30: cannot use reflect.ValueOf(res[1]) (type reflect.Value) as type error in assignment:
reflect.Value does not implement error (missing Error method)
So I figured I needed the Interface() result, but still no luck (using the 2nd assignment commented out above):
./passerror.go:31: cannot use reflect.ValueOf(res[1]).Interface() (type interface {}) as type error in assignment:
interface {} does not implement error (missing Error method)
Finally, a type assertion on the Interface() return value causes a panic:
panic: interface conversion: reflect.Value is not error: missing method Error
No matter what I've tried, I can't seem to escape from the dreaded reflect.Value which prevents me from doing the assignment to ordinary error variable. I have tried Set() also without success, but perhaps incorrectly.
I would be forever grateful for insight and/or the magic incantation to do this.
EDIT
Thanks https://stackoverflow.com/users/965900/mkopriva for the spot on comment. The code requires a real error variable, not just a *error, after which localerr = res[n].Interface().(error) worked perfectly. (also changed function to os.Chdir using a bogus argument to trigger a non-nil error value)
The value returned from Call is a slice of reflect.Values, so there is no need to wrap the result in another reflect.ValueOf call like you do in your example code:
reflect.ValueOf(res[1]) // not what you want
Doing that will change the value's underlying type from error to reflect.Value which is the reason why subsequently calling .Interface().(error) causes the program to panic.
So to fix your code just call .Interface().(error) directly on the result like so:
res[1].Interface().(error)
And, as already pointed out by Cerise Limón, when you do type assertion it's good practice to use the "comma ok" idiom to avoid unnecessary panics.
err, ok := res[1].Interface().(error)
if !ok {
// oops
}
Or, a bit more concise alternative:
if err, ok := res[1].Interface().(error); ok && err != nil {
// it's a non-nil error
}

Using map[string]int as parameter of type map[interface{}]interface{}

I have a function:
func ReturnTuples(map_ map[interface{}]interface{}) [][]interface{} {
In which I'm trying to call like this:
m := make(map[string]int)
m["k1"] = 7
m["k2"] = 13
fmt.Println(ReturnTuples(m))
But I'm getting
cannot use m (type map[string]int) as type map[interface {}]interface {} in argument to ReturnTuples
Shouldn't it work since string and int both implement interface{}?
I've searched and the best I could find was Convert map[interface {}]interface {} to map[string]string but it won't answer why I cannot use m as an argument.
I also believe that if the argument of the function were only interface{} it would work too, since map[something][something] implements interface, right? What is the best way to do it, and why it won't work in my case?
A solution to your problem, simply initiate the map as an empty interface of empty interfaces:
m := map[interface{}]interface{}
then you can assign any type key or value you want in the 'ReturnTuples' function.
playground example
NOTE: remember that if you want to use the values later as the original types, you will need to use type assertion because now they are of type interface{}
You may do something this like this, were anything is one map value which you can get using a for loop:
switch v := anything.(type) {
case string:
fmt.Println(v)
case int32, int64:
fmt.Println(v)
case string:
fmt.Println(v)
case SomeCustomType:
fmt.Println(v)
default:
fmt.Println("unknown")
}
If you are looking for an explanation for the "why"
#ymonad gave a full answer so I wont repeat it again.
hope it make sense
PS: don't get the down votes on the question, a legit one in my eyes...
You can type assert in the function itself.
func test(m interface{},key interface{}) bool { // Map is passed as reference
ma := m.(map[interface{}]interface{})
if _, ok := ma[key]; ok == false {
....
}

Type assertion in go

I have this code snippet:
if (reflect.TypeOf(device).String() == "*types.VirtualDisk") {
disk := device.(types.VirtualDisk)
fmt.Printf("%v - %v \n", "capacityInKB", disk.CapacityInKB)
}
to which I get:
impossible type assertion: types.VirtualDisk does not implement
types.BaseVirtualDevice (GetVirtualDevice method has pointer receiver)
But If I modify it to
if (reflect.TypeOf(device).String() == "*types.VirtualDisk") {
//disk := device.(types.VirtualDisk)
fmt.Printf("%v - %v \n", "capacityInKB", device)//disk.CapacityInKB)
}
It works and prints all the properties of the object. How am I suppose to convert this?
The error hints that the type you want to type assert is *types.VirtualDisk and not types.VirtualDisk.
Also that reflection trick you're trying to do is completely unnecessary, as there is a special form of the type assertion which reports whether the assertion holds.
See this example:
if disk, ok := device.(*types.VirtualDisk); ok {
// Type assertion holds, disk is of type *types.VirtualDisk
// You may use it so
}

Resources