Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go 是否可以更改存储在空接口中的结构的成员_Go - Fatal编程技术网

Go 是否可以更改存储在空接口中的结构的成员

Go 是否可以更改存储在空接口中的结构的成员,go,Go,我有这个: type pair struct { a, b int } 然后我定义了两个变量: x := pair{ 3, 4 } var y interface{} = x 我意识到y不存储x的引用,而是通过以下代码存储其副本: x.b = 7 fmt.Println(x) fmt.Println(y) // got: // {3 7} // {3 4} 另见: 是否有任何方法可以修改y中复制的结构的pair.y成员 尝试此:(失败) 尝试此:(也失败) 将“b”更改为“b”:(

我有这个:

type pair struct {
    a, b int
}
然后我定义了两个变量:

x := pair{ 3, 4 }
var y interface{} = x
我意识到
y
不存储
x
的引用,而是通过以下代码存储其副本:

x.b = 7
fmt.Println(x)
fmt.Println(y)
// got:
// {3 7}
// {3 4}
另见:

是否有任何方法可以修改y中复制的结构的
pair.y
成员

尝试此:(失败)

尝试此:(也失败)

将“b”更改为“b”:(也失败)


更新:

我不打算用y来更改
x.b
。我想单独更改
y
的字段
b


感谢您的帮助,但这不是一个关于值和引用的简单问题。

您需要首先了解指针和地址。使用指针可以深入更改值。如果您希望y与x具有相同的值,它们需要指向相同的地址。

我找到了一个解决方案:

package main

import (
    "unsafe"
    "fmt"
)

type pair struct {
    a, b int
}

func main() {
    x := pair{ 3, 4 }
    var y interface{} = x
    var z interface{} = y

    // change x.b
    x.b = 7

    // change y.b
    addr := (*(*[2]uintptr)(unsafe.Pointer(&y)))[1]
    pp := (*pair)(unsafe.Pointer(addr))
    pp.b = 8

    fmt.Println(x) // {3 7}
    fmt.Println(y) // {3 8}
    fmt.Println(z) // {3 8}
}
这真是一个骇客

有人能提供一个更自然/更习惯的吗


总结:

  • 将具体值指定给空接口会复制该值
  • 将空接口分配给另一个空接口会导致它们共享相同的基础值

我不想更改
x.b
,我想更改
y
中存储的复制结构的成员@brk x根本没有对y的引用。如果您的作用域中只有x,并且希望修改y的一个成员,而该成员不在您的作用域中,您将无法修改,反之亦然,y没有对x的引用等@dlamblin,但y有一个指向复制的结构的引用/指针。现在,自您的更新以来,它变得更清晰了,它可能是:@mgagnon的副本。我不这么认为。我的问题是,当您将结构值分配给空接口(该结构值被复制,并且与原始接口没有关系)时,实际会发生什么,以及如何访问复制的值。我发现这很微妙,很容易被误解。好吧,可能不是完全的复制品,但它展示了如何使用反射来做你想做的事情。你需要思考
y
,(通过使用
&y
)@mgagnon为什么使用y的地址可以让我在值中设置一个字段?因为Go中的所有内容都是通过值传递的,所以如果
reflect.ValueOf()
y
的副本上工作,它不会修改原始的
y
变量。没有自然,这是一种惯用的方法,因为这不是应该做的。go中的所有作业都是副本,因此,事实上,这甚至与共享相同值的
y
z
一样有效,这是一个实现细节。@JimB我同意你的看法。也许我的问题应该重新命名为“在golang中,是否应该将空接口持有的值视为不可变/只读”。但是有没有描述这种行为的规范?如果你同意,那么我不理解这个问题。Go接口是指定的,并且与任何其他值一样进行操作。它是“不可变的”,就像一个字符串是不可变的一样,就像一个字符串一样,你可以通过使用一些不安全的方法来规避这种不可变性。是的,你可以绕过或打破任何你想要的不安全的东西,但这并不意味着它需要,@ Jimb,我认为它们是类似变量的,可以存储任何大小的对象,而不是C++中的代码> const char */c>,它指向常量内存。这里的不安全操作表明,你似乎理解实现,这相当简单,所以我不确定这是怎么一回事。接口当前实现为一个不透明的2字值(不考虑方法集)——一个字表示动态类型,一个字表示指向动态值的指针。你为什么要用这种语言来访问一些未指定的行为,而这些行为你首先可以使用指针?
// panic: reflect: reflect.Value.SetInt using value obtained using unexported field
v := reflect.ValueOf(y).FieldByName("b")
v.SetInt(33)
type pair {
    a, B int
}
// panic: reflect: reflect.Value.SetInt using unaddressable value
v := reflect.ValueOf(y).FieldByName("B")
v.SetInt(33)
package main

import (
    "unsafe"
    "fmt"
)

type pair struct {
    a, b int
}

func main() {
    x := pair{ 3, 4 }
    var y interface{} = x
    var z interface{} = y

    // change x.b
    x.b = 7

    // change y.b
    addr := (*(*[2]uintptr)(unsafe.Pointer(&y)))[1]
    pp := (*pair)(unsafe.Pointer(addr))
    pp.b = 8

    fmt.Println(x) // {3 7}
    fmt.Println(y) // {3 8}
    fmt.Println(z) // {3 8}
}