Gophers,
I'm trying to implement the reflect package of Go and really stuck on one thing.
context - I'm trying to call an API that returns - time.Time and some data in interface{}. This data could be either int/int64 or float32/float64 for the most part. I take the data in interface{} and further create a struct where I keep the interface{} data in interface as reflect promises quite a few fancy things that I could do with the interface
type dataStore struct {
CreateTime time.Time
Data interface{}
}
I then create amap[string][]dataStore to store the data from the API that I fetch.
I'm trying to do the following to get the Float64 values that I know are coming and I want to do some math on them:
x := map[string][]dataStore {}
ReadDatafromAPI(x) // I call the API to read the data into variable x
//Assume map["CA"][]dataStore{{2020-03-31 21:55:52.123456, 123.4567890123e10},}
fmt.Println(x["CA"][0].Data) // This prints the data 123.4567890123e10, no problem
fmt.Println(reflect.ValueOf(x["CA"][0].Data))// this prints reflect.Value
va := reflect.ValueOf(x["CA"][0].Data)
fmt.Println(va.(float64)) // panic: interface conversion: interface {} is reflect.Value, not float64
fmt.Println(va.Interface()) // prints 123.4567890123e10
fmt.Println(va.Kind()) // prints struct
fmt.Println(va.NumField()) // prints 2 and I can fetch the fields using Field(0) and Field(1) -- the data doesn't make sense to me. not sure if those are pointers or what
I have just one objective - to fetch float64 as float64 and int as int. Meaning use reflect the way it is supposed to be used.
any insight would be highly appreciated.
Thanks in advance.
Folks - thanks for all the answers and pointers. I appreciate this! Looks like I still get reflect.Value as the type and not float64/32 as expected. See below:
switch flt := x["CA"][0].Data.(type) {
case float64:
fmt.Println("Data is float64 -> ", flt)
fmt.Printf("Type for flt is %T -> ", flt)
case float32:
fmt.Println("Data is float32 -> ", flt)
fmt.Printf("Type for flt is %T -> ", flt)
default:
fmt.Println("Its default!")
fmt.Printf("Type for flt is %T -> ", flt) // I always get here with reflect.Value as the Type. Never a float64 which is the value store without any question
}
if nflt, ok := x["CA"][0].Data.(float64); ok {
fmt.Println("nflt is float64")
} else {
fmt.Println("nflt is not float64) // I always get to Else
}
You don't need reflection to convert an interface to one of the known types. You need type assertions or a type switch:
if flt, ok:=data.(float64); ok {
// flt is float64
}
if i, ok:=data.(int); ok {
// i is int
}
Or:
switch k:=data.(type) {
case float64:
// Here, k is float64
case int:
// Here, k is int
}
Use a two-value type assertion to get the value without panicking:
d, ok := (x["CA"][0].Data.(float64)
if ok {
// it's a float64, do something with d.
}
or use a type switch:
switch d := x["CA"][0].Data.(type) {
case int:
// d is an int
case float64:
// d is a float64
... and other types as needed
default:
// handle unexpected type
}
Related
Can someone point me in the right direction of this Go's syntax :
(*int)(nil)
If I have a a value of a given type and I want to convert it to, lets say, float64 I can do this :
var num int = 65
fnum := float64(num)
If I have an interface and I want to "cast it" to some type I can do this :
func main() {
concretevalue := dosomething("hello!")
fmt.Printf("%T : %v", concretevalue, concretevalue)
}
func dosomething( v interface{} ) string {
return v.(string)
}
Where does the (*int)(nil) fit? How can I get information about this specific syntax?
It is a type-conversion, same as float64(num), however, because the converted type is a pointer, you need extra parentheses, because otherwise *int(nil) would mean converting nil to int, and then dereferencing it.
Basically I have map[interface{}]interface{} and inside under key "x" value 90 (I printed to confirm that data are correct), but when I do
mymap["x"].(float32)
I get error
interface conversion: interface {} is uint64, not float32
Problem is that I expect float number (produced from as output from another program). I also tried to float32(mymap["x"]) but not success(also tried examples from google with int64 without success).
I am using go version go1.10.1 linux/amd64
this is my script:
switch i := x.(type) {
case nil:
printString("x is nil") // type of i is type of x (interface{})
case int:
printInt(i) // type of i is int
case float64:
printFloat64(i) // type of i is float64
case func(int) float64:
printFunction(i) // type of i is func(int) float64
case bool, string:
printString("type is bool or string") // type of i is type of x (interface{})
default:
printString("don't know the type") // type of i is type of x (interface{})
}
In golang, a number in JSON message is always parsed into float64.
In order to detect if it is actually integer, I am using reflect.TypeOf() to check its type.
Unfortunately there is no constant that represents reflect.Type.
intType := reflect.TypeOf(0)
floatType := reflect.TypeOf(0.0)
myType := reflect.TypeOf(myVar)
if myType == intType {
// do something
}
Is there more elegant solution instead of using 0 or 0.0 to get reflect.Type?
You may also use the Value.Kind() or Type.Kind() method whose possible values are listed as constants in the reflect package, at the doc of the Kind type.
myType := reflect.TypeOf(myVar)
if k := myType.Kind(); k == reflect.Int {
fmt.Println("It's of type int")
} else if k == reflect.Float64 {
fmt.Println("It's of type float64")
}
You can also use it in a switch:
switch myType.Kind() {
case reflect.Int:
fmt.Println("int")
case reflect.Float64:
fmt.Println("float64")
default:
fmt.Println("Some other type")
}
Note that both reflect.Type and reflect.Value has a Kind() method, so you can use it if you start with reflect.ValueOf(myVar) and also if you start with reflect.TypeOf(myVar).
To check if interface is of a specific type you can use type assertion with two return values, the second return value is a boolean indicating if the variable is of the type specified. And unlike with a single return value, it will not panic if the variable is of a wrong type.
if v, ok := myVar.(int); ok {
// type assertion succeeded and v is myVar asserted to type int
} else {
// type assertion failed, myVar wasn't an int
}
If there's more types that you need to check then using a type switch is a good idea:
switch v := myVar.(type) {
case int:
// v has type int
case float64:
// v has type float64
default:
// myVar was something other than int or float64
}
Note however that neither of these actually solve your problem, because like you say, numbers in JSON documents are always parsed into float64s. So if myVar is a parsed JSON number, it will always have type of float64 instead of int.
To solve this, I suggest you use the UseNumber() method of the json.Decoder, which causes the decoder to parse numbers as type Number, instead of float64. Take a look at https://golang.org/pkg/encoding/json/#Number
// Assume myVar is a value decoded with json.Decoder with UseNumber() called
if n, ok := myVar.(json.Number); ok {
// myVar was a number, let's see if its float64 or int64
// Check for int64 first because floats can be parsed as ints but not the other way around
if v, err := n.Int64(); err != nil {
// The number was an integer, v has type of int64
}
if v, err := n.Float64(); err != nil {
// The number was a float, v has type of float64
}
} else {
// myVar wasn't a number at all
}
In go, is it possible to cast variables dynamically somehow?
For example, if a simple cast would be:
var intAge = interfaceAge.(int)
What if I do not know that age is an int in advance? A simple way of writing it would be
var x = getType()
var someTypeAge = interfaceAge(.x)
Is there a way of achieving something like this? The reflect package gives some ways of determining or casting a type at runtime - but I couldn't find anything like the above mentioned (a generic scheme that would work for all types).
No you can't. Go is a static typed language. The type of a variable is determined at compile time.
If you want to determine dynamically the typeof an interface{} you could use type switching:
var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
fmt.Printf("unexpected type %T", t) // %T prints whatever type t has
case bool:
fmt.Printf("boolean %t\n", t) // t has type bool
case int:
fmt.Printf("integer %d\n", t) // t has type int
case *bool:
fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}
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