Go中的不安全指针:函数调用结束会杀死数组

I'm writing a library and I want to return an array (or write to an array) of an unspecific type to the caller. The type can vary, depending on who calls - I can, however, create as many objects of said type from within my function. One way would be that the caller creates an array and the callee fills that - however, there is no way of telling how long this array is going to be. (Is there a way that the callee makes the caller's array bigger? Remember, the callee only sees x interface{}...)

The other way which I chose because I don't see how above is possible, is that the caller gives me the pointer of his specific type and I redirect it to the array of objects which I created.

Below is my solution. My question: why is the array empty after the function call? They are pointing to the same array after my operation, they should be the same. Am I overlooking something? I thought about GC, but it couldn't be that fast, could it?

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

package main

import "unsafe"
import "reflect"
import "log"

func main() {
    var x []string
    log.Printf("before: %v, %p", x, x)
    manipulate(&x)
    log.Printf("after: %v, %p", x, x)
}

func manipulate(target interface{}) {
    new := make([]string, 0, 10)
    new = append(new, "Hello", "World")
    log.Printf("new: %v, %p", new, new)
    p := unsafe.Pointer(reflect.ValueOf(target).Pointer())
    ptr := unsafe.Pointer(reflect.ValueOf(new).Pointer())
    *(*unsafe.Pointer)(p) = ptr
}

First of all, unsafe is usually a bad idea. So is reflection, but unsafe is at least an order of magnitude worse.

Here is your example using pure reflection (http://play.golang.org/p/jTJ6Mhg8q9):

package main

import (
    "log"
    "reflect"
)

func main() {
    var x []string
    log.Printf("before: %v, %p", x, x)
    manipulate(&x)
    log.Printf("after: %v, %p", x, x)
}

func manipulate(target interface{}) {
    t := reflect.Indirect(reflect.ValueOf(target))
    t.Set(reflect.Append(t, reflect.ValueOf("Hello"), reflect.ValueOf("World")))
}

So, why didn't your unsafe way work? Unsafe is extremely tricky and at times requires understanding the internals. First, some misconceptions you have:

  1. You are using arrays: you are not, you are using slices. Slices are a view of an array. They contain within them a pointer to the data, a length, and a capacity. They are stucts internally and not pure pointers.

  2. Pointer returns the pointer only if it is a pointer: it can actually return a value for many types like a slice. From http://golang.org/pkg/reflect/#Value.Pointer:

    If v's Kind is Slice, the returned pointer is to the first element of the slice. If the slice is nil the returned value is 0. If the slice is empty but non-nil the return value is non-zero.

  3. Arrays are pointers: in Go, arrays are actually values. That means they are copied when passed to other functions or assigned. It also means the .Pointer method wouldn't work.

You are assigning a pointer to an array to a slice type. By luck, the implementation of slices used internally has the data pointer first so you are actually setting the internal array pointer used by the slice. I must stress that is is effectively pure accident. Even still, you are not setting the length and capacity of the slice so it still prints zero elements.

Unsafe lets you do things at such a low level that the actual results aren't really defined. It is best to stay away from it unless you really know what you are doing. Even then, be aware that things can can change and what works today may not work in the next version of Go or another implementation.