Why golang reflect.MakeSlice returns un-addressable Value
Asked Answered
B

2

25

check the Snippet below:

http://play.golang.org/p/xusdITxgT-

Why is this happening? Because one of my argument must be a slice address.

Maybe I did not made it clear for everyone.

collection.Find(bson.M{}).All(&result)

The above code is why I need a slice address.

the result variable here is what I need. Now usually I can do this

result := make([]SomeStruct, 10, 10)

But now the SomeStruct is dynamic and I need to create the slice by using reflect.MakeSlice, So

result := reflect.MakeSlice(reflect.SliceOf(SomeType))

And it errors on : result must be a slice address.

Bowline answered 19/8, 2014 at 13:26 Comment(4)
Why should MakeSlice return an addressable slice?Astrodome
I don't know, but I need one. that's why I am here. Can you tell me is there any way to to this?Bowline
@Bowline Please tell us more about what you are going to do with that addressable slice and why you need it. This seems a bit odd. A code sample would be nice.Longlegged
@Longlegged check out my new editBowline
L
62

How to get a pointer to a slice using reflection

The simplest solution is probably to use reflect.New() to create the pointer (full example on play):

my := &My{}

// Create a slice to begin with
myType := reflect.TypeOf(my)
slice := reflect.MakeSlice(reflect.SliceOf(myType), 10, 10)

// Create a pointer to a slice value and set it to the slice
x := reflect.New(slice.Type())
x.Elem().Set(slice)

collection.Find(bson.M{}).All(x.Interface())

Note the x.Interface() that was pointed out by other answers as well. This prevents that instead of the reflect.Value the actual value of x is passed to All().

Why does reflect.MakeSlice return an un-addressable Value?

A loose definition of addressability in Go is that you can take the address of something and are guaranteed that this address points to somewhere meaningful. If you allocate something on the stack in the body of a function, the address of the allocated value will, at some point in time, not be accessible anymore. Therefore, this value is not addressable. In most cases, Go moves local stack variables to the heap if they are returned or otherwise promoted to the outside, but at runtime this is not done. Therefore, CanAddr() returns only true when:

A value is addressable if it is an element of a slice, an element of an addressable array, a field of an addressable struct, or the result of dereferencing a pointer.

The stated types all have one thing in common: they guarantee that what they hold will be accessible from everywhere and point to a meaningful value in memory. You have neither a slice element, nor a pointer, nor any of the other mentioned things since you created a local slice using reflect.MakeSlice. The elements of said slice would be addressable though (since the slice's memory resides on the heap).

Why a pointer to a slice?

The main question for me in this case was, why does the API of mgo require a pointer to a slice for iter.All? After all, slices are reference types and for changes in the provided data set, no pointer is necessary. But then it occurred to me that most of the time the function appends to the slice. Appending leads to memory allocation, memory allocation leads to copying the old data to new memory, new memory means a new address which needs to be communicated to the caller.

This behaviour is illustrated in this example on play. In essence:

// Works. Uses available storage of the slice.
    resultv.Index(1).Set(a)

// Executes but changes are lost:   
//  reflect.Append(resultv, a)

// Does not work: reflect.Value.Set using unaddressable value
//  resultv.Set(reflect.Append(resultv, a))
Longlegged answered 19/8, 2014 at 14:46 Comment(2)
I would probably still have hair if I had found this answer earlier. :-)Friedman
outstanding detailed answerDiarist
F
3

I think you're after the interface() Method here, but I'm not sure why you would need to create a slice back of a type via reflect.

package main

import (
    "fmt"
    "reflect"
)

type My struct {
    Name string
    Id   int
}

func main() {
    my := &My{}
    myType := reflect.TypeOf(my)
    slice := reflect.MakeSlice(reflect.SliceOf(myType), 10, 10).Interface()
    p := slice.([]*My)

    fmt.Printf("%T %d %d\n", p, len(p), cap(p))
}

Produces:

[]*main.My 10 10

Playground

Finke answered 19/8, 2014 at 14:3 Comment(1)
@FUZxxl, Thanks for the edit. I think nemo's answer is much more comprehensive and should probably be accepted as the correct one.Finke

© 2022 - 2024 — McMap. All rights reserved.