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

Dan*_*yel -1 arrays pointers go

我正在编写一个库,我想将一个非特定类型的数组(或写入数组)返回给调用者.类型可以根据谁调用而变化 - 但是,我可以在我的函数中创建所述类型的所有对象.一种方法是调用者创建一个数组并且被调用者填充它 - 但是,没有办法知道这个数组将会有多长.(有没有办法让被叫方使调用者的数组更大?记住,被调用者只看到x interface{}......)

我选择的另一种方式,因为我看不出上面的可能性,是调用者给了我特定类型的指针,然后我将它重定向到我创建的对象数组.

以下是我的解决方案.我的问题:函数调用后为什么数组为空?它们在我的操作后指向同一个数组,它们应该是相同的.我忽略了什么吗?我想过GC,但它不会那么快,不是吗?

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
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*erg 6

首先,不安全通常是一个坏主意.反思也是如此,但不安全至少要差一个数量级.

以下是使用纯反射的示例(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")))
}
Run Code Online (Sandbox Code Playgroud)

那么,为什么你的不安全方式不起作用?不安全是非常棘手的,有时需要了解内部.首先,你有一些误解:

  1. 您正在使用数组:您不是,您正在使用切片.切片是数组的视图.它们包含指向数据的指针,长度和容量.它们是内部结构而不是纯指针.

  2. 仅当指针是指针时,指针才返回指针:它实际上可以返回许多类型的值,如切片.来自http://golang.org/pkg/reflect/#Value.Pointer:

    如果v的Kind是Slice,则返回的指针指向切片的第一个元素.如果切片为nil,则返回值为0.如果切片为空但非nil,则返回值为非零.

  3. 数组是指针:在Go中,数组实际上是值.这意味着它们在传递给其他函数或分配时被复制.这也意味着.Pointer方法不起作用.

您正在将指向数组的指针指定给切片类型.幸运的是,内部使用的切片的实现首先具有数据指针,因此您实际上设置了切片使用的内部数组指针.我必须强调,这实际上是纯粹的意外.即便如此,您还没有设置切片的长度和容量,因此它仍然会打印零元素.

不安全允许您以如此低的水平执行操作,以至于实际结果未真正定义.除非你真的知道自己在做什么,否则最好远离它.即便如此,请注意事情可以改变,今天有效的方法可能无法在下一版Go或其他实现中发挥作用.