Any sensible solution to the lack of array/slice covariance in Go?

前提是你 提交于 2019-11-26 21:42:25

问题


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?


回答1:


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.




回答2:


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.




回答3:


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,")
}



回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/3839335/any-sensible-solution-to-the-lack-of-array-slice-covariance-in-go

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!