2

How come this works:

slice := make([]string, 0, 10)
sliceptr := &slice

this too:

sliceptr := &[]string{"foo","bar","baz"}

But this doesn't:

sliceaddrval := reflect.ValueOf([]string{"foo","bar","baz"}).Addr()

It panics with: reflect.Value.Addr of unaddressable value


EDIT: Overall what I'm trying to do is take a struct that is of an unknown type, make a slice of structs of that type and return a pointer to it (I'm using github.com/jmoiron/modl which requires a pointer to slice to populate with results from a SQL query).

Brad Peabody
  • 10,917
  • 9
  • 44
  • 63

1 Answers1

5

reflect.Value takes an interface{}, and an interface{} to a value can't be used to change the original. Otherwise, you could end up with code changing data in your struct when you didn't even intend to pass it a pointer. (Or, in this case, changing the length of a slice that was passed by value.) So if you take the address you'd have to do it before the ValueOf.

To make a pointer to a slice that you can to pass to a package that will append to it (like modl or Google App Engine GetMulti), you'd use something like http://play.golang.org/p/1ZXsqjrqa3, copied here:

package main

import (
    "fmt"
    "reflect"
)

type row struct { i, j int }

func main() {
    aRow := row{}

    valueType := reflect.ValueOf(aRow).Type()
    slicePtrVal := reflect.New(reflect.SliceOf(valueType))
    slicePtrIface := slicePtrVal.Interface()

    getQueryResults(slicePtrIface)
    fmt.Println(slicePtrIface)
}

// standing in for `modl` or whatever populates the slice
func getQueryResults(slicePtr interface{}) {
    sPtr := slicePtr.(*[]row)
    (*sPtr) = append((*sPtr), row{1,3}) 
}

Appending to a slice in a reflect.Value yourself takes another few lines of reflect, but it sounds like the package you're working with takes care of that part for you. For general info, code to do the append is at http://play.golang.org/p/m3-xFYc6ON and below:

package main

import (
    "fmt"
    "reflect"
)

type row struct { i, j int }

func main() {
    aRow := row{}

    // make a pointer to an empty slice
    rowType := reflect.ValueOf(aRow).Type()
    slicePtrVal := reflect.New(reflect.SliceOf(rowType))
    slicePtrIface := slicePtrVal.Interface()

    // append a zero row to it
    rowVal := reflect.Zero(rowType)
    sliceVal := reflect.Indirect(slicePtrVal)
    sliceVal.Set(reflect.Append(sliceVal, rowVal))

    fmt.Println(slicePtrIface)
}
twotwotwo
  • 28,310
  • 8
  • 69
  • 56
  • 1
    actually, think the correct answer is similar in spirit but slightly different (it's that an `interface{}` holding a value type can't be used like a pointer), editing – twotwotwo Nov 05 '14 at 19:16
  • alright. what you're saying about the slice header makes sense, i guess i just don't get what the pointer is pointing to when you do this: &[]string{} or rather why there seems to be no reflect equivalent of that. – Brad Peabody Nov 05 '14 at 19:23
  • I added a bit to the question. Basically what I'm looking for is "make an empty slice of type X (a struct that "someone else" provides) and get pointer to it", which I need in order to collect results from a query. – Brad Peabody Nov 05 '14 at 19:28
  • Got it--about to update this to not lie about the reason it doesn't work :) then update with how to reach the more specific goal if I can. – twotwotwo Nov 05 '14 at 19:31
  • So it turns out it's hard to get a pointer via `.Addr` even when you just created the slice (go figure), but `New()` comes to the rescue here. Updated with code. – twotwotwo Nov 05 '14 at 20:10
  • awesome! yeah that's exactly it. – Brad Peabody Nov 05 '14 at 20:12