Can someone explain why this doesn't work and how can we return slices of interfaces, []interface{}, from functions like showed in the example?
import (
"fmt"
)
func main() {
var test []string
Test(&test)
fmt.Println(test)
}
func Test(t interface{}) {
a := []interface{}{"first", "second"}
fmt.Println(a)
t = a
}
Example of running code can be found here:
https://play.golang.org/p/vcEGHSdWrjv
BTW, this is the func I'm trying to extract data from: https://godoc.org/github.com/mongodb/mongo-go-driver/mongo#Collection.Distinct
Note: the type we expect is not always of type []string, I'm just using string as an example here.
Thanks!
In you example, you are not trying to "return" a slice but rather you seem to be looking to modify the argument to point to a new slice of strings.
The way you are doing it does not work cause in Go, arguments are passed by value.
When you do this:
t = a
t is a copy of the &test you are sending to the function as an argument.
So modifying t does not change your test variable.
You need to pass in the address of a pointer in order to be able to modify what the pointer points to.
Try this way:
func main() {
var test *[]string
Test(&test)
fmt.Println(*test)
}
func Test(t interface{}) {
a := []string{"first", "second"}
fmt.Println(a)
*t.(**[]string) = &a
}
Output:
[first second]
[first second]
Playground: https://play.golang.org/p/tliMrmliykp
Because an []string and a []interface{} are different types, you cannot assign one to the other.
You must copy the slice to convert []interface{} to a slice of some specific type. If you know that the []interface{} always contains string values, then use the following:
func stringSlice() []string {
a := []interface{}{"first", "second"} // query result
fmt.Println(a)
result := make([]string, len(a))
for i := range a {
var ok bool
result[i], ok = a[i].(string)
if !ok {
// handle error with unexpected type
}
}
return result
}
If the result can have arbitrary element types, then use reflection to copy the slice:
func anySlice(result interface{}) {
a := []interface{}{"first", "second"} // query result
slice := reflect.ValueOf(result).Elem()
elementType := slice.Type().Elem()
for _, v := range a {
rv := reflect.ValueOf(v)
if !rv.Type().AssignableTo(elementType) {
// handle error with unexpected type
}
slice.Set(reflect.Append(slice, rv))
}
}
Use it like this:
var s []string
anySlice(&s)
playground example
Related
func main() {
strSlice := []string{"a", "b", "c"}
f(strSlice)
}
func f(slice interface{}) {
anySlice, isSlice := slice.([]interface{})
fmt.Printf("isSlice = %t, anySlice = %#v\n", isSlice, anySlice)
}
Playground: https://play.golang.org/p/UN25mIOqmOd
This program prints isSlice = false, anySlice = []interface {}(nil). Why is that?
I would have expected this type asssertion to be possible.
And: Is there a way to dynamically check that interface{} is a slice of something?
The type assertion fails because a []string is not an []interface{}. See the FAQ for details.
Use the reflect package to determine if the concrete value in an interface is a slice:
func f(slice interface{}) {
isSlice := reflect.ValueOf(slice).Kind() == reflect.Slice
fmt.Printf("isSlice = %t, anySlice = %#v\n", isSlice, slice)
}
Run it on the playground.
You can also use the reflect package to iterate through the values:
v := reflect.ValueOf(slice)
isSlice := v.Kind() == reflect.Slice
if isSlice {
for i := 0; i < v.Len(); i++ {
fmt.Printf("%d: %v\n", i, v.Index(i).Interface())
}
}
Run it on the playground.
This program prints isSlice = false, anySlice = []interface {}(nil). Why is that?
because slice doesn't contain a []interface{}, it contains a []string. Those are different types, and Go doesn't give you any notion of covariant container types.
And: Is there a way to dynamically check that interface{} is a slice of something?
Yes, you can use reflection:
func f(slice interface{}) {
typ := reflect.TypeOf(slice)
if typ.Kind() == reflect.Slice {
elemType := typ.Elem()
fmt.Println("slice of", elemType.Name())
} else {
fmt.Println("not a slice")
}
}
Actually doing anything with that information may be more involved.
Notice that if you change your assertion to: slice.([]string) things will work as expected. This makes sense when you consider how Go handles string to interface{} type assertions. The following will result in a compilation error:
package main
func main() {
s := "some string"
i := s.(interface{})
}
Error:
invalid type assertion: s.(<inter>) (non-interface type string on left)
It makes sense that going from []string to []interface{} should also fail since Go does not treat strings as interface{} types.
type mcat struct {
ID int
}
type cat struct {
Name string
M mcat
}
func getValue(path string, mcat cat){
//throuth struct path get the value
}
func main(){
mycat := cat{"cat", mcat{1}}
id := getvalue("/M/ID", mycat)
}
Can I do this by reflecting to get a value based on the field name?
You may do what you want with the Value.FieldByName() function. Just range over the parts of the path which may be splitted using strings.Split().
Here's an example:
func getValue(i interface{}, path string) interface{} {
v := reflect.ValueOf(i)
for _, field := range strings.Split(path[1:], "/") {
v = v.FieldByName(field)
}
return v.Interface()
}
func main() {
mycat := cat{"cat", mcat{1}}
id := getValue(mycat, "/M/ID")
fmt.Println(id)
}
It outputs (try it on the Go Playground):
1
Some things to note:
The above solution works for all struct types, not just with cat. Checks if the passed value is a struct or the field exists is omitted.
I cut of the leading / of the path with a slice expression: path[1:] so we don't have to deal with an empty field name inside the loop.
The above getValue() returns the result as an interface{}. If you need the ID as an int, you may use type assertion like this:
var intID int
intID = id.(int)
Also note that it may be nicer / more useful to use a variadic parameter for the path:
func getValue(i interface{}, path ...string) interface{} {
v := reflect.ValueOf(i)
for _, field := range path {
v = v.FieldByName(field)
}
return v.Interface()
}
func main() {
mycat := cat{"cat", mcat{1}}
id := getValue(mycat, "M", "ID")
fmt.Println(id)
}
Output is the same. Try this one on the Go Playground.
My input is an interface{}, and I know it can be an array of any type.
I'd like to read one of the elements of my input, so I try to convert my interface{} into an []interface{}, but go will give me the following error:
panic: interface conversion: interface {} is []map[string]int, not []interface {}
How can I do that conversion? (without reflect if possible).
Playground test
Thanks
The solution involving the reflect package.
package main
import (
"fmt"
"reflect"
)
func main() {
var v interface{} = []string{"a", "b", "c"}
var out []interface{}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Slice {
for i := 0; i < rv.Len(); i++ {
out = append(out, rv.Index(i).Interface())
}
}
fmt.Println(out)
}
// Output:
// [a b c]
I'm actually working on this right now as my issue involves taking something from a json object (map[string]interface{}) which may or may not contain a particular key ({"someKey": [a, b, c, ...]) and if it does contain that key then we want to take that (which will necessarily be interface{} type) and convert it to []interface{}. The method I've found so far is to use json marshall/unmarshall. This seems a little hacky to me, will update if I find a more elegant solution. Til then, you can have my method:
https://play.golang.org/p/4VAwQQE4O0b
type a map[string]interface{}
type b []string
func main() {
obj := a{
"someKey": b{"a", "b", "c"},
}
if obj["someKey"] != nil { // check the value exists
var someArr []interface{}
//marshal interface to byte and then unmarshal to []interface{}
somebytes, _ := json.Marshal(obj["someKey"])
err := json.Unmarshal(somebytes, &someArr)
if err != nil {
fmt.Println("Error in unmarshal")
}
fmt.Println(someArr)
}
}
How can I do that conversion? (without reflect if possible).
Please consider type switches.
Reflection is expensive.
func toSlice(i interface{}) []interface{} {
var out []interface{}
switch v := i.(type) {
case []interface{}:
for x := 0; x < len(v); x++ {
out = append(out, v[x])
}
default:
fmt.Printf("invalid type: %T\n", v)
}
return out
}
The point of the interface is to define the behaviour you want to use, if you use an empty interface, you know nothing about the types in that slice.
If you want to print it, you can use println or printf with no conversion.
If you want to access it, and must allow any type, you can use reflect (slow and complex to use).
If you want to acess it, and use common behaviour/ data that you can define functions for, define an interface, e.g. :
type Doer interface {
Do() error
}
parentStruct := []Doer{...}
testStruct.Do()
If none of that works, wait for Go 2 and generics.
For anyone finding this in 2022, now that we have generics you can do it like this:
func convertSlice[T any](data []T) []interface{} {
output := make([]interface{}, len(data))
for idx, item := range data {
output[idx] = item
}
return output
}
I think what you are looking is type assertion
package main
import (
"fmt"
)
func main() {
parentStruct := map[string]interface{}{
"test": []map[string]int{
{"a": 1, "b": 2},
{"c": 3},
},
}
testStruct := parentStruct["test"].([]map[string]int)
fmt.Println(testStruct)
}
read this link: https://golang.org/ref/spec#Type_assertions
https://play.golang.org/p/81uL2hgrN3l
I'm parsing a JSON object which contains an array of strings :
var ii interface{}
json := "{\"aString\": [\"aaa_111\", \"bbb_222\"], \"whatever\":\"ccc\"}"
err := json.Unmarshal([]byte(json), &ii)
if err != nil {
log.Fatal(err)
}
data := ii.(map[string]interface{})
fmt.Println(data["aString"]) // outputs: ["aaa_111" "bbb_222"]
I tried to convert data["aString"] to []string to be able to loop over it, but it fails :
test := []string(data["aString"]).([]string)
fmt.Println(test) // panic -> interface conversion:
// interface is string, not []string
How can I convert data["aString"] ?
edit:
I didn't express myself properly. If I print data, I have such map :
map[aString:["BBB-222","AAA-111"] whatever:ccc]
I want to loop over aString (to manipule each array entry). But I can't find how, because aString is type interface {} :
for i, v := range aString { // <-- fails
// ...
fmt.Println(i, v)
}
That's why I want to convert aString. I don't want to convert a string which looks like an array to an array.
I recommend you move away from this implementation in general. Your json may vary but you can easily use objects and avoid all this type unsafe nonsense.
Anyway, that conversion doesn't work because the types inside the slice are not string, they're also interface{}. You have to iterate the collection then do a type assertion on each item like so:
aInterface := data["aString"].([]interface{})
aString := make([]string, len(aInterface))
for i, v := range aInterface {
aString[i] = v.(string)
}
Is it what you need?
package main
import (
"fmt"
"encoding/json"
)
func main() {
js := "{\"aString\": [\"aaa_111\", \"bbb_222\"], \"whatever\":\"ccc\"}"
a := make(map[string]interface{})
json.Unmarshal([]byte(js), &a)
for _, v := range a["aString"].([]interface{}) {
str := v.(string)
fmt.Println(str)
}
}
Check on Go Playground
For another approach, you can use a struct instead:
package main
import (
"encoding/json"
"fmt"
)
func main() {
s := []byte(`{"aString": ["aaa_111", "bbb_222"], "whatever":"ccc"}`)
var t struct {
Astring []string
Whatever string
}
json.Unmarshal(s, &t)
fmt.Printf("%+v\n", t) // {Astring:[aaa_111 bbb_222] Whatever:ccc}
}
I found this question with this great answers:
How to find a type of a object in Golang?
I played around with the answer and tried to get the name of a struct in the same way:
package main
import (
"fmt"
"reflect"
)
type Ab struct {
}
func getType(myvar interface{}) string {
return reflect.TypeOf(myvar).Name()
}
func main() {
fmt.Println("Hello, playground")
tst := "string"
tst2 := 10
tst3 := 1.2
tst4 := new(Ab)
fmt.Println(getType(tst))
fmt.Println(getType(tst2))
fmt.Println(getType(tst3))
fmt.Println(getType(tst4))
}
Go playground: http://play.golang.org/p/tD8mygvETH
But the output is:
Hello, playground
string
int
float64
Program exited.
Expected output would be:
Hello, playground
string
int
float64
Ab
Program exited.
I tried to figure out by reading the documentation but didn't find the issue about that. So, sorry for the very general question, but:
What's the reason, reflect.TypeOf().Name() does not work with (this) struct(s)?
In your example you pass a value of pointer type (*Ab), not a struct type.
Sticking to Type.Name()
If it is not a pointer, Type.Name() will properly return Ab. In case of pointer if you still want the struct's name, you can use Type.Elem() to get the element's type:
func getType(myvar interface{}) string {
if t := reflect.TypeOf(myvar); t.Kind() == reflect.Ptr {
return "*" + t.Elem().Name()
} else {
return t.Name()
}
}
Testing it:
tst4 := Ab{}
tst5 := new(Ab)
fmt.Println(getType(tst4))
fmt.Println(getType(tst5))
Output (try your modified example on the Go Playground):
Ab
*Ab
Note:
Note that as Type.Name() does not resolve pointers, it would not work if the value passed is a pointer to pointer, e.g. **Ab, while as Type.String() automatically resolves pointers, would work in this case too.
We can easily make our getType() function to work with **Ab too (or with any depth of pointers):
func getType(myvar interface{}) (res string) {
t := reflect.TypeOf(myvar)
for t.Kind() == reflect.Ptr {
t = t.Elem()
res += "*"
}
return res + t.Name()
}
Calling it with values:
tst4 := Ab{}
tst5 := new(Ab)
tst6 := &tst5 // type of **Ab
tst7 := &tst6 // type of ***Ab
Output (try it on the Go Playground):
Ab
*Ab
**Ab
***Ab
Using Type.String()
A simpler and better approach would be to use Type.String() instead of Type.Name() which automatically handles pointers and also includes package name. E.g.:
func getType(myvar interface{}) string {
return reflect.TypeOf(myvar).String()
}
For the modified example it outputs:
string
int
float64
main.Ab
*main.Ab
Try this variant on the Go Playground.
fmt has a cool %T tag as well
package main
import (
"fmt"
"net/http"
)
type Potato struct {
}
func main() {
fmt.Printf("I have a %T, an %T and a %T\n", Potato{}, http.StatusMultipleChoices, &http.Response{})
}
outputs I have a main.Potato, an int and a *http.Response
https://play.golang.org/p/6z7_0BSitm
The problem is new returns pointer, following should get the desired result.
package main
import (
"fmt"
"reflect"
)
type Ab struct {
}
func getType(myvar interface{}) {
valueOf := reflect.ValueOf(myvar)
if valueOf.Type().Kind() == reflect.Ptr {
fmt.Println(reflect.Indirect(valueOf).Type().Name())
} else {
fmt.Println(valueOf.Type().Name())
}
}
func main() {
fmt.Println("Hello, playground")
tst := "string"
tst2 := 10
tst3 := 1.2
tst4 := new(Ab)
getType(tst)
getType(tst2)
getType(tst3)
getType(tst4)
}
Output is
Hello, playground
string
int
float64
Ab