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

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
}

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.

How to handle missing variable panic properly?

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.

Go not receiving error will trigger panic but receiving error will not trigger panic

With the following code:
var i interface{} = "hello"
f, ok := i.(float64)
fmt.Println(f, ok)
f = i.(float64) // panic
fmt.Println(f)
Why is not catching error will cause panic but catching error will not cause a panic?
Any documentation or blogpost explaining this concept?
This is a type assertion & is documented in the Go spec here:
The value of ok is true if the assertion holds. Otherwise it is false
and the value of v is the zero value for type T. No run-time panic
occurs in this case.
With the check in place & there is a type mismatch, the value will be set to the "zero" value of type float32 (a number) so 0.
Without the runtime check, you should be absolutely sure the type will match, and the spec dictates a panic will occur if not.
This pattern is no different than basic error checking e.g.
// v, err := someapi(). // Should check err ...
v, _ := someapi() // deliberately ignore error
v.SomeMethod() // ... Panic as v probably nil if there was an error

Best practice for result values when returning an error in Go

If your function returns both a value type and also an error type, is it "the go way" to make sure the value type is niled/zero-valued when the error type is non-nil?
Example:
func mayError() ([]string, error) {
...
}
Should the []string return value be nil if the error is not nil?
Generally speaking, if a function failed to complete a task, its return value should be treated as unreliable. Because errors in go are values, the caller may ignore the error it returns. For exmaple:
foo := myType{
Bar: 123,
Foo: "some string",
}
b, _ := json.Marshal(foo)
I'm ignoring the error because it's a type I've created, and I know it can be marshalled. However, it's considered bad practice either way. All the same, now imagine someone calling your function:
slice, _ := mayError()
And your function, after adding 2 elements to the slice, errors. Returning the partial slice could, and probably will, lead to buggy behaviour further down the line. That makes code hard to debug. On balance, I'd say it's best to return a nil slice and an error in that case. If the code looks like this:
slice, _ := mayError() // returns nil, someErr
// panic
if slice[0] != "" {
}
At least the error shows up immediately, and you'll see any errors returned by mayError are being ignored. This makes the code way easier to debug/maintain/fix.

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