zhaozhi
zhaozhi

Reputation: 1581

how to convert a interface{} to its underlying type

I have some code in the playground: sample code

I pass a two dimension string slice into a function test, which can accept variadic arguments, and in test() I can get the first argument's underlying type, but how can I convert it back to its underlying type? because I have to iterate on its underlying type

I don't like to hard code it like:

if reflect.TypeOf(args[0]).String() == "[][]string" {
    val := args[0].([][]string)
}

the question is if I know its type string is "[][]string" or something else, how can I convert it to the type?

I post the full code here, and add some comments:

package main

import (
    "reflect"
    "fmt"
)

func test(args ...interface{}) {
    fmt.Println("type", reflect.TypeOf(args[0]))
    // here will be a compile error, because args[0]'type now is interface{}, 
    // not a slice, though it's underlying type is slice

    for i, v := range args[0] {    

    }

    // so, how could I convert args[0] to [][]string when I get its type 
    // string "[][]string" ?
    // I know use type assertion is possible, but you must guess the possible 
    // type the args[0] will be.
    // is there a way to do it from a type's string representation to the 
    // actual type?
    // so I can write code like this: 
    // val := args[0].(reflect.TypeOf(args[0]).String()), which is very general


}
func main() {
    arr := [][]string{{"asd", "sd", "rt"}, {"34","gf","gf"}}
    test(arr)
}

Upvotes: 2

Views: 2934

Answers (3)

LeGEC
LeGEC

Reputation: 52151

Another variation on ANisus answer :

package main

import "fmt"

func main() {
    var x interface{} = [][]string{{"Hello"}, {"World", "!"}}

    if y, ok := x.([][]string); ok {
        fmt.Printf("%v", y)
    }
}

http://play.golang.org/p/tGYbhzuUnr


Otherwise, use the reflect package :

import (
    "fmt"
    "reflect"
)

func process(i interface{}) {
    fmt.Printf("Processing %v\n", i)

    if reflect.TypeOf(i).Kind() == reflect.Slice {
        v := reflect.ValueOf(i)
        for i := 0; i < v.Len(); i++ {
            process(v.Index(i).Interface())
        }
    }
}

func main() {
var x = [][]string{{"Hello"}, {"World", "!"}}
var y = []int{2,3,5,7,9}
var z = 'H'

process(x)
process(y)
process(z)
}

http://play.golang.org/p/MAqIgxzLuC

Upvotes: 4

nemo
nemo

Reputation: 57737

It is not possible to use a dynamic value for a type assertion, so

foo.(reflect.TypeOf(bar))

or something similar will not work. This is because types are not first class citizens and can't be stored in a variable.

You always have to write explictly which type you want the interface value to be, either by using a type assertion or a type switch.

Upvotes: 1

ANisus
ANisus

Reputation: 78065

I am not entirely sure what you wish to achieve.

If you pass in a [][]string as the first argument, in order to iterate over it, you must do a type assertion or type switch:

switch val := args[0].(type) {
case [][]string:
    // Do what you want with val which is of type [][]string
}

You can read more about type switches in the Go specifications: http://golang.org/ref/spec#Switch_statements

If you try to pass the [][]string slice unchanged to test:

test(arr...) // Attempt to  pass the array unchanged.

this will fail. This is due to [][]string not being assignable to []interface{}. The Go specification states:

If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.

Upvotes: 3

Related Questions