-1

I've got a function to which I want to feed different kinds of slices after which I want to loop over them and print their contents. The following code works:

func plot(data interface{}){
    fmt.Println(data)
    //fmt.Println(len(data))
}

func main() {
    l := []int{1, 4, 3}
    plot(l)
}

But when I uncomment the line in which I print the length of the slice, I get an error saying invalid argument data (type interface {}) for len.

Any idea how I would be able to get the length of the slice so that I can loop over it?

kramer65
  • 50,427
  • 120
  • 308
  • 488
  • @CeriseLimón per the question "I want to feed different kinds of slices". – Adrian Sep 06 '17 at 17:45
  • If the argument is an empty interface, you'll have to use the `reflect` package to do what you want. See https://golang.org/pkg/reflect. You might want to think about defining a specific interface that defines methods that perform whatever operations you need to perform on the objects you're passing in. – Andy Schweig Sep 06 '17 at 17:45
  • If you're working with different kinds of slices, you must already be using reflection or asserting them to concrete types. How are you using `data` already that you can't get the length? – JimB Sep 06 '17 at 17:48
  • Wouldn't it just be easier to pass the length in `func plot (data interface{}, length int)`? Assuming it's not an `interface{}` type before passing it. – RayfenWindspear Sep 06 '17 at 18:16
  • Possible duplicate of https://stackoverflow.com/questions/14025833/range-over-interface-which-stores-a-slice – Charlie Tumahai Sep 06 '17 at 18:21

2 Answers2

3

You should try to avoid using interface{} whenever possible. What you want to do can be done with reflection, but reflection is a necessary evil. It is really good for marshalling, but should be used sparingly. If you still want to use reflect, you can do something like this:

func plot(data interface{}) {
    s := reflect.ValueOf(data)
    if s.Kind() != reflect.Slice {
        panic("plot() given a non-slice type")
    }

    for i := 0; i < s.Len(); i++ {
        v := s.Index(i)
        ...
    }
}

Even after doing this, v is a reflect.Value. You will then need to somehow convert that to something useful. Luckily, Value has many methods that can be used to convert it. In this case, v.Int() would return the value as an int64.

Stephen Weinberg
  • 51,320
  • 14
  • 134
  • 113
1

As hinted in comments you would have to use reflection to do this, something like the following:

var sliceLen int

switch reflect.TypeOf(data).Kind() {

    case reflect.Slice:
        sliceLen = s.Len();
    default:
        //error here, unexpected
    }
}

Although go provides reflection to do these little tricks when you need to (as well as many other uses), it is often better to avoid wherever possible to maintain compiler type safety and performance, consider the pros/cons of having separate functions for different data types over this approach

SwiftD
  • 5,769
  • 6
  • 43
  • 67