按引用设置接口{}参数

时间:2014-11-01 11:27:30

标签: interface go

我很难理解如何设置已作为指针传递的接口值。我正在尝试按照以下方式完成某些事情:

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]
}

我需要做什么才能使我的节目输出

num: 9001

编辑:使用reflect

是否存在可能的全面解决方案

5 个答案:

答案 0 :(得分:12)

您可以使用reflect模拟AppEngine数据存储区界面;通常我会说最小化反射,但你(和AppEngine和其他ORM)在这里没有其他很好的选择来呈现你想要的界面。对于模仿Get你的事情:

  • 使用reflect.Value
  • 获得ValueOf()
  • 获取您要创建的内容的类型
  • 使用reflect.Zero
  • 创建它
  • 可选择使用reflect.Field()等填写一些数据
  • 使用reflect.Indirect()Value.Set()通过指针设置原始文件。

通过指针将结构清零的一个简单示例位于http://play.golang.org/p/g7dNlrG_vr并在此处复制:

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)
}

要模拟GetMulti,您需要更多步骤来处理切片。一个例子是http://play.golang.org/p/G_6jit2t-2及以下:

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))
    }
}

在实时代码中(与您正在进行的测试相反),使用类型切换(如Martin所建议的)更快,以便为每种类型运行专门的本机代码;如果您按类型有不同的行为,这也可能很方便。 GetMulti的示例位于http://play.golang.org/p/q-9WyUqv6P及以下位置:

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))
    }
}

你甚至可以轻而易举地将两者结合起来;编写常见类型的自定义代码,并在默认情况下调用基于慢reflect的版本。在http://play.golang.org/p/6qw52B7eC3进行演示(不要复制,因为它是上面两者的简单拼接)。

碰巧有another recent question关于如何将值传递给GetMulti,而不是模仿GetMulti本身,如果出现的话。


更多供一般性参考而非回答:

“缺乏参考传递”是有用的知识,但也需要一些细化。 Go有指针,以及其他类型,如包含数据指针的切片。没有“通过引用传递”的意义只是Go永远不会隐式地将值参数(intstruct)更改为指针。 C ++引用参数完全相同:C ++ void f(i int&) { i++; }在调用者中更改i,而调用者没有在调用点显式传入指针。 func (i int) { i++ }没有。

在Go中,您可以查看传递给函数调用的类型,并告诉它可以更改哪些数据。使用C ++引用参数或某些语言的“按引用传递”语义,任何调用可能会更改本地;如果不查看声明,你无法分辨。

为了避免不必要的数据复制,在slice,string,map,interface和channel值的实现中已经存在指针。在这些类型中,指针,切片和贴图实际上允许您通过它们修改数据。此外,与C ++一样,Go的this - 类接收器参数可以是调用代码中没有显式&的指针。 Russ Cox's godata postthis summary on when you need a pointer or not中有更多相关信息。

答案 1 :(得分:5)

  

The Go Programming Language Specification

     

Calls

     

在函数调用中,将计算函数值和参数   通常的顺序。在评估它们之后,调用的参数   通过值传递给函数,并且被调用的函数开始   执行。函数的返回参数按值传递   当函数返回时返回调用函数。

在Go中,一切都按价值传递;什么都没有通过引用传递。因此,传递一个指针。例如,

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]
}

输出:

num: 9001

答案 2 :(得分:1)

首先:绝对没有&#34;通过引用传递&#34;在Go。没有。您可以 的内容是传递指针。当Go中的所有内容都按值传递时,此指针值将按值传递。

第二:不是传入指向接口的指针并修改pointees值(可行而是丑陋),你可以返回值(更好)。

第三:没有类型断言就不能(即不是没有反射或不安全)。 并且你永远不应该(在你掌握Go和接口&#34;之前的意义上),使用指向接口的指针。

第五:如果你的解决方案需要interface{},你可能会做错事。您确定某些(非空)接口无法描述您的实体吗?

那就是这样的事情。

func main() {
    var num int
    Stuff["key"] = 9001
    num = get("key").(int)
}
func get(k string) interface{}{
    return Stuff[k]
}

答案 3 :(得分:1)

Martin Gallagher解决方案运行完美,但正如他所说,你不能在Golang中使用泛型,所以代码看起来有点难看。我想另一种解决方案是使用始终interface{}作为类型,然后在程序中强制转换(或检查类型)。这样的事情:http://play.golang.org/p/0o20jToXHV

答案 4 :(得分:0)

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

如果没有泛型,您必须为每种支持的类型实现切换{}。