无法从[[] interface {}`断言类型[[] string`

I am trying to process some data retrieved from mongodb (mgo).

Unfortunately I am unable to assert the correct type for a list of strings. The function I am working on is the following:

func generate_version_histogram(userStats []interface{}) map[string]int {
    var histogram map[string]int
    for _, _u := range userStats {
        u := _u.(bson.M)
        for _, version := range (u["v"]).([]string) {
            if _, alreadyhere := histogram[version]; alreadyhere {
                histogram[version] += 1
            } else {
                histogram[version] = 1
            }
        }
    }
    return histogram
}

Unfortunately I am getting this following run-time panic:

interface conversion: interface is []interface {}, not []string

Any idea on why this is happening? How can I retrieve those strings?

This is a common mistake with Go.

The reason is as follows: in Go []interface{} is not an interface, it's a slice type, whose elements are each the interface{} type.

Because each element is a interface{}, rather than, say, an int or Foo, more memory is taken up by each element (interface{} needs to store the underlying type, and the value contained). Therefore, it's not possible to directly convert a []interface value into a []string or []T value.

How do you convert []interface{} into []string, then?

The solution is quite simple — you convert each element.

package main

import "fmt"

func main() {
    foo := []interface{}{"a", "b", "c"}

    // we want to convert foo to a []string
    out := []string{}

    for _, v := range foo {
        // using a type assertion, convert v to a string
        out = append(out, v.(string))
    }

    fmt.Println(out)
}

Runnable example here.

A []interface{} will never be a []string. A slice has a backing array, and those elements have to be of a certain size. This may or may not be exactly correct depending on implementation details, but each element of an []interface{} will contain an interface{}. If all of these interface{} are "really" strings, they won't be the strings themselves but rather a wrapper over the string or a pointer to it. Thus you have to convert each individual element of a []interface{} yourself.

stringSlice := make([]string, len(u["v"]))
for i,raw := range u["v"] {
    str,ok := raw.(string)
    if !ok {
        // Something is wrong, not a string like we expected
    }
    stringSlice[i] = str
}