8

I am having difficulty understanding how to set an interface value that has been passed as a pointer. I am trying to accomplish something along the lines of this:

import "fmt"

var Stuff map[string]interface{}

func main() {
    var num int
    Stuff["key"] = 9001
    get("key", &num)
    fmt.Println("num:", num)
}

func get(k string, v interface{}) {
    *v = Stuff[k]
}

What would I have to do to make my program output be

num: 9001

Edit: is there a possible catch-all solution using reflect?

BlinkyTop
  • 1,124
  • 3
  • 14
  • 19
  • 1
    Just guessing, but maybe you're trying to do `Get` from the appengine datastore. It's odd duck for returning through a pointer (for GetMulti, a pointer to a slice); it seems like maybe they were trying to keep the mess inside the implementation rather than returning an `interface{}` and having the caller convert. To do anything like that, you will have to use `reflect`, and it's no fun. If you want to handle specific slice types (exclusively or as a "fast path" for a few types), use a type assertion like PeterSO said (or a type switch for a few types). – twotwotwo Nov 02 '14 at 06:32
  • You may have seen a blog post called "the Laws of Reflection." The first rule of reflection should be avoid reflection. :) But, admittedly needed here or anywhere your code might need to read or build structs of arbitrary types. – twotwotwo Nov 02 '14 at 06:42
  • 1
    Nice one. It is appengine datastore. I totally should have said this but I'm writing a testing implementation of what is essentially a tight wrapper over datastore. – BlinkyTop Nov 02 '14 at 15:49
  • 1
    I figured I'd be forced to reflect so I am instead allowing a subset of types with assertion... for the time being – BlinkyTop Nov 02 '14 at 15:50

5 Answers5

14

You can emulate the AppEngine datastore interface using reflect; usually I say minimize reflection, but you (and AppEngine and other ORMs) have no other great option here to present the interface you want. For something emulating Get you:

  • get a reflect.Value with ValueOf()
  • get the type of the thing you want to create
  • create it with reflect.Zero
  • optionally fill in some data with reflect.Field(), etc.
  • use reflect.Indirect() and Value.Set() to set the original through the pointer.

A trivial example that just zeroes a struct through a pointer is at http://play.golang.org/p/g7dNlrG_vr and copied here:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    i := 1
    clear(&i)
    fmt.Println(i)
}

func clear(dst interface{}) {
    // ValueOf to enter reflect-land
    dstPtrValue := reflect.ValueOf(dst)
    // need the type to create a value
    dstPtrType := dstPtrValue.Type()
    // *T -> T, crashes if not a ptr
    dstType := dstPtrType.Elem()
    // the *dst in *dst = zero
    dstValue := reflect.Indirect(dstPtrValue)
    // the zero in *dst = zero
    zeroValue := reflect.Zero(dstType)
    // the = in *dst = 0
    dstValue.Set(zeroValue)
}

For emulating GetMulti you need more steps to work with the slice. An example is at http://play.golang.org/p/G_6jit2t-2 and below:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    s := []int{}
    getMultiZeroes(&s, 10)
    fmt.Println(s)
}

func getMultiZeroes(slicePtrIface interface{}, howMany int) {
    // enter `reflect`-land
    slicePtrValue := reflect.ValueOf(slicePtrIface)
    // get the type
    slicePtrType := slicePtrValue.Type()
    // navigate from `*[]T` to `T`
    sliceElemType := slicePtrType.Elem().Elem() // crashes if input type not `*[]T`
    // we'll need this to Append() to
    sliceValue := reflect.Indirect(slicePtrValue)
    // and this to Append()
    sliceElemValue := reflect.Zero(sliceElemType)

    // append requested number of zeroes
    for i := 0; i < howMany; i++ {
        // s := append(s, v)
        sliceValue.Set(reflect.Append(sliceValue, sliceElemValue))
    }
}

In live code (as opposed to testing like you're doing), it'd be faster to use a type switch (as Martin suggested) so that specialized native code runs for each type; that might also be handy if you have different behavior by type. An example for GetMulti is at http://play.golang.org/p/q-9WyUqv6P and below:

package main

import "fmt"

func main() {
    s := []int{}
    getZeroes(&s)
    fmt.Println(s)

    fails := []float32{}
    getZeroes(&fails)
}

func getZeroes(slicePtrIface interface{}) {
    switch sp := slicePtrIface.(type) {
    case *[]int:
        (*sp) = append((*sp), 0, 0)
    case *[]string:
        (*sp) = append((*sp), "", "")
    default:
        panic(fmt.Sprintf("getZeroes: passed type %T, which is not a pointer to a slice of a supported type", slicePtrIface))
    }
}

You could even trivially combine the two; write custom code for common types and call the slow reflect-based version in the default case. Demo at http://play.golang.org/p/6qw52B7eC3 (not copying because it's a such a simple stitching together of the two above).

There happened to be another recent question on how to make a value to pass to GetMulti, rather than emulating the GetMulti itself, if that comes up.


More for general reference than to answer this:

"Go lacks pass by reference" is useful to know, but also needs some elaboration. Go has pointers, and other types like slices that contain pointers to data. The sense in which there isn't "pass by reference" is just that Go will never change a value argument (int, struct) into a pointer implicitly. C++ reference arguments do exactly that: C++ void f(i int&) { i++; } changes i in the caller without the caller explicitly passing in a pointer at the callsite. func (i int) { i++ } doesn't.

In Go, you can look at the types passed to a function call and tell what data it can change. With C++ reference arguments or some languages' "pass by reference" semantics, any call might change locals; you can't tell without looking up the declarations.

For purposes of avoiding unnecessary copying of data, there are already pointers in the implementations of slice, string, map, interface, and channel values. Of those types, pointers, slices, and maps will actually let you modify data through them. Also, like in C++, Go's this-like receiver parameter can be a pointer without an explicit & in the calling code. There's more about this in Russ Cox's godata post and this summary on when you need a pointer or not.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
twotwotwo
  • 28,310
  • 8
  • 69
  • 56
  • just realized you might be doing `Get` and this only talked about `GetMulti`, so I added an example without the extra slice-related bits. – twotwotwo Nov 09 '14 at 05:56
6

The Go Programming Language Specification

Calls

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the calling function when the function returns.

In Go everything is passed by value; nothing is passed by reference. Therefore, pass a pointer. For example,

package main

import "fmt"

var Stuff map[string]interface{}

func main() {
    Stuff = make(map[string]interface{})
    Stuff["key"] = 9001
    var value interface{}
    get("key", &value)
    num := value.(int)
    fmt.Println("num:", num)
}

func get(k string, v interface{}) {
    *v.(*interface{}) = Stuff[k]
}

Output:

num: 9001
peterSO
  • 158,998
  • 31
  • 281
  • 276
1

First: There is absolutely no concept of "pass by reference" in Go. There isn't. What you can do is pass around a pointer. This pointer value is passed by value as everything in Go is passed by value.

Second: Instead of passing in a pointer to an interface and modify the pointees value (doable but ugly) you could return the value (much nicer).

Third: It cannot (i.e. not without reflection or unsafe) be done without type assertions. And you should never (in the sense of "no until you mastered Go and interfaces") use pointer to interface.

Fifth: If your solution requires interface{} you might be doing something wrong. Are you sure your entities are not describable by some (non empty) interface?

That said, something like that works.

func main() {
    var num int
    Stuff["key"] = 9001
    num = get("key").(int)
}
func get(k string) interface{}{
    return Stuff[k]
}
Volker
  • 40,468
  • 7
  • 81
  • 87
  • 1
    I would much rather return the value from the function, but I am trying to satisfy an already standardized interface (one based off of app engine datastore to be specific) – BlinkyTop Nov 01 '14 at 12:27
1

Martin Gallagher solution works perfectly, but as he said, you can't use generics in Golang, so code looks a bit ugly. I guess another solution is to use always interface{} as the type and then cast (or check the type) in your program. Something like this: http://play.golang.org/p/0o20jToXHV

Magd Kudama
  • 3,229
  • 2
  • 21
  • 25
0

http://play.golang.org/p/kx5HvEiOm9

Without generics you will have to implement the switch {} for each of your supported types.

Martin Gallagher
  • 4,444
  • 2
  • 28
  • 26