16

I'm noob in Go :) so my question may be stupid, but can't find answer, so.

I need a function:

func name (v interface{}) {
    if is_slice() {
        for _, i := range v {
            my_var := i.(MyInterface)
            ... do smth
        }
    } else {
        my_var := v.(MyInterface)
        ... do smth
    }
}

How can I do is_slice in Go? Appreciate any help.

icza
  • 389,944
  • 63
  • 907
  • 827
Alexander
  • 450
  • 1
  • 4
  • 12
  • Actually my problem is that v is slice of pointers. After some investigate I think it's better iterate over collection in first place, at least implementation I've seen used such approach. Some copy/paste boilerplate code, but I think it won't be a problem for now – Alexander Oct 31 '16 at 17:30
  • 1
    Possible duplicate of [range over interface{} which stores a slice](https://stackoverflow.com/questions/14025833/range-over-interface-which-stores-a-slice) – Tarion Dec 17 '18 at 12:59

4 Answers4

17

The is_slice method can be something like this:

func IsSlice(v interface{}) bool {
    return reflect.TypeOf(v).Kind() == reflect.Slice
}

Can also put additional condition of reflect.TypeOf(v).Kind() == reflect.Array if required.

pratpor
  • 1,954
  • 1
  • 27
  • 46
  • 2
    This is the actual answer to the question. Thanks! – Shlomi Aug 16 '21 at 23:33
  • Is it possible to use reflect to convert an interface{} to an array (or slice) of interface{} ? – Alex SHP Jun 23 '22 at 18:22
  • // InterfaceSlice converts a slice of any object to a slice of interfaces of those internal objects // if in is not an addressable item, it will panic func InterfaceSlice(in any) (o []any) { if in == nil { return } v := reflect.ValueOf(in) for i := 0; i < v.Len(); i++ { o = append(o, v.Index(i).Interface()) } return o } – spekary Apr 05 '23 at 15:10
15

In your case the type switch is the simplest and most convenient solution:

func name(v interface{}) {
    switch x := v.(type) {
    case []MyInterface:
        fmt.Println("[]MyInterface, len:", len(x))
        for _, i := range x {
            fmt.Println(i)
        }
    case MyInterface:
        fmt.Println("MyInterface:", x)
    default:
        fmt.Printf("Unsupported type: %T\n", x)
    }
}

The case branches enumerate the possible types, and inside them the x variable will already be of that type, so you can use it so.

Testing it:

type MyInterface interface {
    io.Writer
}

var i MyInterface = os.Stdout
name(i)
var s = []MyInterface{i, i}
name(s)
name("something else")

Output (try it on the Go Playground):

MyInterface: &{0x1040e110}
[]MyInterface, len: 2
&{0x1040e110}
&{0x1040e110}
Unsupported type: string

For a single type check you may also use type assertion:

if x, ok := v.([]MyInterface); ok {
    // x is of type []MyInterface
    for _, i := range x {
        fmt.Println(i)
    }
} else {
    // x is not of type []MyInterface or it is nil
}

There are also other ways, using package reflect you can write a more general (and slower) solution, but if you're just starting Go, you shouldn't dig into reflection yet.

icza
  • 389,944
  • 63
  • 907
  • 827
3

icza's answer is correct, but is not recommended by go creators:

interface{} says nothing

A better approach may be to define a function for each type you have:

func name(v MyInterface) {
    // do something
}

func names(vs []MyInterface) {
    for _, v := range(vs) {
        name(v)
    }
}
Community
  • 1
  • 1
Lucas Gabriel Sánchez
  • 40,116
  • 20
  • 56
  • 83
  • 3
    Wondering why creators of fmt package didn't create print function for every type? – Alexander Oct 31 '16 at 17:24
  • Thanks for interesting link. – Alexander Oct 31 '16 at 17:36
  • 1
    @Alexander you have a somewhat "print for every type" implemented as the Print verbs: %s for strings,%p for pointers, %d for integers of base 10, etc, and you can use %s and implement your own String() string function to print your own type – Lucas Gabriel Sánchez Oct 31 '16 at 18:17
  • Please mind that in this case you won't be able to directly pass into your function a slice of values, because slice of interfaces is not an interface: https://github.com/golang/go/wiki/InterfaceSlice – Daniel Titkov Jan 28 '20 at 16:05
0

From https://blog.golang.org/json

Decoding arbitrary data

Consider this JSON data, stored in the variable b:

b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)

Without knowing this data's structure, we can decode it into an interface{} value with Unmarshal:

var f interface{} err := json.Unmarshal(b, &f) At this point the Go value in f would be a map whose keys are strings and whose values are themselves stored as empty interface values:

f = map[string]interface{}{
    "Name": "Wednesday",
    "Age":  6,
    "Parents": []interface{}{
        "Gomez",
        "Morticia",
    },
}

To access this data we can use a type assertion to access f's underlying map[string]interface{}:

m := f.(map[string]interface{})

We can then iterate through the map with a range statement and use a type switch to access its values as their concrete types:

for k, v := range m {
    switch vv := v.(type) {
    case string:
        fmt.Println(k, "is string", vv)
    case float64:
        fmt.Println(k, "is float64", vv)
    case []interface{}:
        fmt.Println(k, "is an array:")
        for i, u := range vv {
            fmt.Println(i, u)
        }
    default:
        fmt.Println(k, "is of a type I don't know how to handle")
    }
}

In this way you can work with unknown JSON data while still enjoying the benefits of type safety.

Cal
  • 359
  • 3
  • 5