Type assertion in go - 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
}

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.

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
}

Type assertion when returning an interface

I'm new to golang; however based on my current knowledge I understand that a value-type and a reference-type can both fulfill an interface. But it seems in regards to type assertion, how you return a struct does matter. See the following:
package main
import (
"fmt"
)
type SomeError interface {
Error() string
}
type ConcreteError struct{}
func (ConcreteError) Error() string {
return "?"
}
func returnPointer() SomeError {
return &ConcreteError{}
}
func returnVal() SomeError {
return ConcreteError{}
}
func main() {
pointer := returnPointer()
value := returnVal()
_, pointerWithPointer := pointer.(*ConcreteError);
_, pointerWithValue := pointer.(ConcreteError);
_, valueWithValue := value.(ConcreteError);
_, valueWithPointer := value.(*ConcreteError)
fmt.Printf("Returning a pointer, assert using (*ConcreteError): %v\n", pointerWithPointer); // true
fmt.Printf("Returning a pointer, assert using (ConcreteError): %v\n", pointerWithValue); // false
fmt.Printf("Returning a value, assert using (ConcreteError): %v\n", valueWithValue); // true
fmt.Printf("Returning a value, assert using (*ConcreteError): %v\n", valueWithPointer); // false
}
So if my understanding is correct, the user needs to know how the struct is returned to correctly assert its type?
I'm going to guess and assume the standard practice in golang is to always return a pointer to a struct(i.e like *PathError)?
link to play: here
So if my understanding is correct, the user needs to know how the struct is returned to correctly assert its type?
It depends. If you need the type assertion to pass - you surely need to know the exact type of the value. a and *a are different types.
I'm going to guess and assume the standard practice in golang is to always return a pointer to a struct(i.e like *PathError)?
No.

cannot convert data (type interface {}) to type string: need type assertion

I am pretty new to go and I was playing with this notify package.
At first I had code that looked like this:
func doit(w http.ResponseWriter, r *http.Request) {
notify.Post("my_event", "Hello World!")
fmt.Fprint(w, "+OK")
}
I wanted to append newline to Hello World! but not in the function doit above, because that would be pretty trivial, but in the handler afterwards like this below:
func handler(w http.ResponseWriter, r *http.Request) {
myEventChan := make(chan interface{})
notify.Start("my_event", myEventChan)
data := <-myEventChan
fmt.Fprint(w, data + "\n")
}
After go run:
$ go run lp.go
# command-line-arguments
./lp.go:15: invalid operation: data + "\n" (mismatched types interface {} and string)
After a little bit of Googling I found this question on SO.
Then I updated my code to:
func handler(w http.ResponseWriter, r *http.Request) {
myEventChan := make(chan interface{})
notify.Start("my_event", myEventChan)
data := <-myEventChan
s:= data.(string) + "\n"
fmt.Fprint(w, s)
}
Is this what I was supposed to do? My compiler errors are gone so I guess that's pretty good? Is this efficient? Should you do it differently?
According to the Go specification:
For an expression x of interface type and a type T, the primary expression x.(T) asserts that x is not nil and that the value stored in x is of type T.
A "type assertion" allows you to declare an interface value contains a certain concrete type or that its concrete type satisfies another interface.
In your example, you were asserting data (type interface{}) has the concrete type string. If you are wrong, the program will panic at runtime. You do not need to worry about efficiency, checking just requires comparing two pointer values.
If you were unsure if it was a string or not, you could test using the two return syntax.
str, ok := data.(string)
If data is not a string, ok will be false. It is then common to wrap such a statement into an if statement like so:
if str, ok := data.(string); ok {
/* act on str */
} else {
/* not string */
}
Type Assertion
This is known as type assertion in golang, and it is a common practice.
Here is the explanation from a tour of go:
A type assertion provides access to an interface value's underlying concrete value.
t := i.(T)
This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t.
If i does not hold a T, the statement will trigger a panic.
To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded.
t, ok := i.(T)
If i holds a T, then t will be the underlying value and ok will be true.
If not, ok will be false and t will be the zero value of type T, and no panic occurs.
NOTE: value i should be interface type.
Pitfalls
Even if i is an interface type, []i is not interface type. As a result, in order to convert []i to its value type, we have to do it individually:
// var items []i
for _, item := range items {
value, ok := item.(T)
dosomethingWith(value)
}
Performance
As for performance, it can be slower than direct access to the actual value as show in this stackoverflow answer.
//an easy way:
str := fmt.Sprint(data)
As asked for by #ρяσѕρєя an explanation can be found at https://golang.org/pkg/fmt/#Sprint. Related explanations can be found at https://stackoverflow.com/a/44027953/12817546 and at https://stackoverflow.com/a/42302709/12817546. Here is #Yuanbo's answer in full.
package main
import "fmt"
func main() {
var data interface{} = 2
str := fmt.Sprint(data)
fmt.Println(str)
}
In addition to other answers, I think it's good to have a look at "type switch":
package main
import "fmt"
func printType(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("type of %v is %v\n", i, v)
// type of 21 is int
case string:
fmt.Printf("type of %v is %v\n", i, v)
// type of hello is string
default:
fmt.Printf("type of %v is %v\n", i, v)
// type of true is bool
}
}
func main() {
printType(21)
printType("hello")
printType(true)
}
I hope it helps.
More information: https://go.dev/tour/methods/16

Resources