Interface 通过引用设置接口{}参数

Interface 通过引用设置接口{}参数,interface,go,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] } 我要

我很难理解如何设置作为指针传递的接口值。我正试图实现以下目标:

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
的全面解决方案?

首先:Go中绝对没有“参考传递”的概念。没有。你能做的就是传递一个指针。这个指针值是按值传递的,因为Go中的所有内容都是按值传递的

第二:您可以返回值(更好),而不是将指针传递到接口并修改pointees值(可行但难看)

第三:如果没有类型断言,它就不能完成(即没有反射或不安全)。 而且,您永远不应该(在“掌握Go和interface之前不应该”)使用指向interface的指针

第五:如果您的解决方案需要
接口{}
您可能做错了什么。是否确定某些(非空)接口无法描述实体

也就是说,类似的方法是有效的

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


如果没有泛型,您必须为每个受支持的类型实现开关{}。

Martin Gallagher解决方案工作得很好,但正如他所说,您不能在Golang中使用泛型,因此代码看起来有点难看。我想另一个解决方案是始终使用
接口{}
作为类型,然后在程序中强制转换(或检查类型)。大概是这样的:

在函数调用中,函数值和参数在 通常的顺序。在对它们求值之后,调用的参数 按值传递给函数,被调用函数开始 执行。函数的返回参数按值传递 当函数返回时返回到调用函数

在围棋中,一切都是通过价值传递的;没有任何内容是通过引用传递的。因此,传递一个指针。比如说,

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 编号:9001
您可以使用模拟AppEngine数据存储接口;通常我说最小化反射,但是您(以及AppEngine和其他ORM)在这里没有其他好的选项来展示您想要的接口。对于模拟
Get
的内容,请执行以下操作:

  • 使用
    ValueOf()获取一个
    reflect.Value
  • 获取要创建的对象的类型
  • 使用
    reflect.Zero创建它
  • 可以选择使用
    reflect.Field()
    等填充一些数据
  • 使用
    reflect.Indirect()
    Value.Set()
    通过指针设置原始值
一个通过指针将结构归零的简单示例位于,并复制到此处:

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
,您需要更多步骤来使用切片。下面是一个例子:

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))
    }
}
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))
    }
}
你甚至可以简单地将两者结合起来;为常见类型编写自定义代码,并在默认情况下调用基于slow
reflect
的版本。演示在(不是复制,因为这是一个如此简单的缝合在一起的两个以上)

如果出现这样的情况,那么应该考虑如何将值传递给
GetMulti
,而不是模拟
GetMulti
本身


更多的是供一般参考,而不是回答这个问题:

“Go缺乏参考传递”是有用的,但也需要一些详细说明。Go有指针和其他类型,如包含数据指针的切片。没有“按引用传递”的意思是Go永远不会隐式地将值参数(
int
struct
)更改为指针。C++引用参数完全做到:C++调用ValueF(i int){i++;}在调用方中更改<代码> i <代码>,而调用方在指针站点中不显式传递指针。code>func(i int){i++}
没有

在Go中,您可以查看传递给函数调用的类型,并告诉它可以更改哪些数据。使用C++引用参数或一些语言的“通过引用”语义,任何调用都可能改变本地人;你不查声明就说不出来


为了避免不必要的数据复制,切片、字符串、映射、接口和通道值的实现中已经存在指针。在这些类型中,指针、切片和映射实际上允许您通过它们修改数据。此外,像C++一样,GO的<代码>这个< /COR>类接收器参数可以是一个指针,在调用代码中没有明确的<代码>和<代码>。在和中有更多关于这方面的内容

我更愿意从函数返回值,但我试图满足一个已经标准化的接口(具体来说,基于app engine数据存储的接口)只是猜测,但可能您正在尝试从appengine数据存储中执行
获取
。通过指针返回(对于GetMulti,是指向切片的指针),这是一个奇怪的duck;看起来,他们可能是试图将混乱保存在实现中,而不是返回
接口{}
并让调用方转换。要做到这一点,你必须使用
反射
,这一点都不好玩。如果您想处理特定的切片类型(独占或作为少数类型的“快速路径”),请使用PeterSO所说的类型断言(或少数类型的类型开关)。您可能已经看到一篇名为“反射定律”的博客文章。反射的第一条规则应该是避免反射。:)但是,不可否认,在这里或任何地方,您的代码可能需要读取或构建任意类型的结构。它是appengine数据存储。我完全应该这么说,但我正在编写一个测试实现,它本质上是对数据存储的紧密包装。我想我会被迫进行反思,所以我允许使用断言的类型子集。。。目前,我刚刚意识到您可能正在执行
Get
,而这只涉及
GetMulti
,因此我添加了