Reputation: 31
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!
Upvotes: 0
Views: 2422
Reputation: 121149
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)
Upvotes: 1
Reputation: 12403
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
Upvotes: 2