misterbee
misterbee

Reputation: 5182

Convenience function to convert slice to tuple?

In Python, one can write code like this, to assign multiple values from a list:

(a, b, c, d) = [1,2,3,4]

Is there a similar set of Go library function for slices? That is, I can do this: http://play.golang.org/p/DY1Bi5omm1

package main

func get3(s []interface{}) (
    a interface{},
    b interface{},
    c interface{},
    rest []interface{}) {
  return s[0],s[1],s[2],s[4:]
}

func main() {  
  s := make([]interface{},5);
  for i :=0 ; i < 5; i++ { s[i] = i}
  a,b,c,_ := get3(s)

  print(a.(int))
  print(b.(int))
  print(c.(int))
}

Is there a standard gophery way to do this?

And is there a way around the interface{} ugliness?

Upvotes: 2

Views: 1227

Answers (2)

Toni C&#225;rdenas
Toni C&#225;rdenas

Reputation: 6848

Not like that; you would need dynamic typing or parametric polymorphism, which are not available in Go. The closest I can think about is by fiddling with reflect, like this: http://play.golang.org/p/-K4jh2nZjq

// src is supposed to be []T.
// dst are supposed to be &T, except the last one, which must be a 'rest' &[]T (or nil for discarding).
// There must not be more dst vars than elements in src.
func extract(src interface{}, dst ...interface{}) {
    srcV := reflect.ValueOf(src)
    // Iterate over dst vars until we run out of them.
    i := 0
    for i = 0; i < len(dst)-1; i++ {
        reflect.Indirect(reflect.ValueOf(dst[i])).Set(srcV.Index(i))
    }

    // Now, the rest.
    restDst := dst[i]
    if restDst == nil {
        return
    }
    restV := reflect.ValueOf(restDst)
    indirectRest := reflect.Indirect(restV)
    l := srcV.Len() - i
    indirectRest.Set(reflect.MakeSlice(restV.Type().Elem(), 0, l))
    for ; i < srcV.Len(); i++ {
        itemV := srcV.Index(i)
        indirectRest.Set(reflect.Append(indirectRest, itemV))
    }
    return
}

Which then you call like:

sl := []int{1, 2, 3, 4, 5, 6} // int or any other type
var a, b, c int
var rest []int
extract(sl, &a, &b, &c, &rest)

So the ugliness doesn't get out the function.

But note that all that happens at runtime, so it's not safe nor efficient and definitely is not idiomatic Go.

Upvotes: 2

vinaut
vinaut

Reputation: 2446

I don't think you can, not in an idiomatic/clean way at least. You CAN do multiple assignments, but you will have to pass individual values either directly or with a closure:

package main

import (
  "fmt"
)

func valuesFromList(list[]int,startFrom int) func() int  {
  i:=startFrom
  return func() int {
    ret := list[i]
    i++
    return ret
  }
}


func main () {
  list := []int{0,1,2,3,4,5,6,7,8,9}
  yield := valuesFromList(list,5)

  //This works
  a,b,c := yield(),yield(),yield()
  fmt.Println(a)
  fmt.Println(b)
  fmt.Println(c)

  //This also works
  d,e,f := list[0],list[1],list[2]
  fmt.Println(d)
  fmt.Println(e)
  fmt.Println(f)
  //This won't work
  //g,h,i:= list[7:9]
}

Upvotes: 2

Related Questions