Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code Duplication in Go Type Switches

Tags:

go

Just getting started writing Go code and I ran into an interesting problem.

Is there a way to easily iterate over the items in an array that is brought in as an empty interface without code duplication? Consider the following:

function(someArr interface{}){
  switch someArr.(type){
    case []int :
        arr := (someArr).([]int)
        for i := range (arr) {
          // CODE
        }

    case []string :
        arr := (someArr).([]string)
        for i := range (arr) {
          // CODE
        }
  } 
}

In this example the code in CODE is exactly the same. However, I cannot take it out of the switch because the type assertion arr would fall out of scope. Similarly, I can't define arr before the switch because I don't know what type it will be. It's possible that this just cannot be done. In that case, what's a better idiom for this kind of thing when I'm, say, parsing JSON with an irregular schema (some arrays of ints, some arrays or strings)?

like image 656
Erik Hinton Avatar asked Dec 15 '22 07:12

Erik Hinton


2 Answers

Your example is not idiomatic Go code, even though the idiomatic one lexically seems violating the DRY principle as well.

The key point to understand is that 'x' is a separate, differently typed variable in each type case:

function(someArr interface{}){
        switch x := someArr.(type) {
        case []int:
                for i := range x {
                        // CODE
                }
        case []string:
                for i := range x {
                        // CODE
                }
        }
}
like image 59
zzzz Avatar answered Jan 07 '23 21:01

zzzz


You can use the reflect package to iterate over arbitrary slices. But implementing the special cases (like []int) explicitly is generally faster and is often done in addition to avoid reflection in common cases.

package main

import "fmt"
import "reflect"

func foo(values interface{}) {
    rv := reflect.ValueOf(values)
    if rv.Kind() != reflect.Slice {
        return
    }
    n := rv.Len()
    for i := 0; i < n; i++ {
        value := rv.Index(i).Interface()
        fmt.Println(value)
    }
}

func main() {
    foo([]int{1, 3, 3, 7})
}

Edit: I'm not sure why somebody has down voted the question and my answer, but there are cases where you need to deal with code like that. Even the standard library contains plenty of it, take a look at "fmt", "gob", "json", "xml" and "template" for example. The questioner might face a similar problem.

like image 28
tux21b Avatar answered Jan 07 '23 22:01

tux21b