11

The problem I've just faced is what to do in the following case:

func printItems(header string, items []interface{}, fmtString string) {
  // ...
}

func main() {
  var iarr = []int{1, 2, 3}
  var farr = []float{1.0, 2.0, 3.0}
  printItems("Integer array:", iarr, "")
  printItems("Float array:", farr, "")
}

Go has no generics and doesn't allow to use collection covariance:

prog.go:26: cannot use iarr (type []int) as type []interface { } in function argument      
prog.go:27: cannot use farr (type []float) as type []interface { } in function argument

Ideas?

PGene
  • 442
  • 4
  • 9

5 Answers5

11

I'm surprised nobody mentioned using an interface to solve the problem, which is a very idiomatic approach, if a little clunky:

package main

import "fmt"

type List interface {
    At(i int) interface{}
    Len() int
}

func printItems(header string, items List) {
    for i := 0; i < items.Len(); i++ {
        fmt.Print(items.At(i), " ")
    }
    fmt.Println()
}

type IntList []int
type FloatList []float64

func (il IntList)   At(i int) interface{} { return il[i] }
func (fl FloatList) At(i int) interface{} { return fl[i] }

func (il IntList)   Len() int { return len(il) }
func (fl FloatList) Len() int { return len(fl) }

func main() {
    var iarr = []int{1, 2, 3}
    var farr = []float64{1.0, 2.0, 3.0}
    printItems("Integer array:", IntList(iarr))
    printItems("Float array:", FloatList(farr))
}

By defining the size and indexing of the list for each type, you can access them "generically". Of course, generics would still be nice so you don't have to do this.

SteveMcQwark
  • 2,169
  • 16
  • 9
7

There's not really a way to do this right now without either

  1. Making your []int and []float both into []interface{}.
  2. Making printItems accept interface{} instead of []interface{} and then use reflection, similar to what the fmt package does.

Neither solution is pretty.

Evan Shaw
  • 23,839
  • 7
  • 70
  • 61
5

An example of using reflection:

package main

import (
    "fmt"
    "reflect"
    "strings"
    "container/vector"
)

func printItems(header string, items interface{}, fmtString string) {
    value, ok := reflect.NewValue(items).(reflect.ArrayOrSliceValue)
    if !ok {
        panic("Not an array or slice")
    }

    stringBuilder := new(vector.StringVector)
    stringBuilder.Push(header)

    n := value.Len()
    for i := 0; i < n; i++ {
        stringBuilder.Push(fmt.Sprintf(fmtString, value.Elem(i).Interface()))
    }

    fmt.Println(strings.Join(*stringBuilder, ""))
}

func main() {
    var iarr = []int{1, 2, 3}
    var farr = []float{1.0, 2.0, 3.0}

    printItems("Integer array:", iarr, " %d,")
    printItems("Float array:", farr, " %.1f,")
}
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
2
package main

import "fmt"

func printItems(header string, items interface{}, fmtString string) {
  if intItems, ok := items.([]int); ok {
    fmt.Println(header, intItems)
  } else if floatItems, ok := items.([]float64); ok {
    fmt.Println(header, floatItems)
  }
}

func main() {
  var iarr = []int{1, 2, 3}
  var farr = []float64{1.0, 2.0, 3.0}
  printItems("Integer array:", iarr, "")
  printItems("Float array:", farr, "")
}

IMHO, more elegant then solution using reflect.

Wuvist
  • 641
  • 1
  • 5
  • 7
0
package main

func printItems(header string, items interface{}, fmtString string) {
  // ...
}

func main() {
  var iarr = []int{1, 2, 3}
  var farr = []float{1.0, 2.0, 3.0}
  printItems("Integer array:", iarr, "")
  printItems("Float array:", farr, "")
}

Take a look at similar functions, like fmt.Printf(), in the core Go package documentation and source code.

peterSO
  • 158,998
  • 31
  • 281
  • 276
  • Thanks for reference. Yet, this is not about the essence of the question. Technically, I can replace iarr of ints with iarr of interface{} and my printItems() function will work just fine. It's positively not the nicety I'm interested in. I also took a look into fmt package and "saw and beheld" a lot of reflection underneath. When you need to sew on a button you don't usually use a hammer. Reflection is an abnormal approach, so admit your point at a stretch. – PGene Oct 01 '10 at 21:13
  • 1
    Sorry, but I don't think you're being fair here. If you didn't want answers involving reflection, you should have made that clear in your question. The "essence of the question" is answering it, which this poster and others have done very well. How is it "an abnormal approach" when it's the approach the designers of the language took when writing the print functions in the standard library? The mind boggles. Ugh, does Stack Overflow really not allow linebreaks in comments? That's really horrible, sorry if this comes out unreeadable. – cmccabe Sep 12 '12 at 08:21