-5

I have this:

type pair struct {
    a, b int
}

Then I define two variables:

x := pair{ 3, 4 }
var y interface{} = x

I realize that y doesn't store a reference of x but a copy of it via the following code:

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

Also see: https://github.com/golang/go/blob/master/src/runtime/iface.go#L359

Is there any way to modify pair.y member of the copied struct in y?

Tried this: (fail)

// cannot assign to y.(pair).b
y.(pair).b = 7

Tried this: (also fail)

// panic: reflect: reflect.Value.SetInt using value obtained using unexported field
v := reflect.ValueOf(y).FieldByName("b")
v.SetInt(33)

Change "b" to "B": (also fail)

type pair {
    a, B int
}
// panic: reflect: reflect.Value.SetInt using unaddressable value
v := reflect.ValueOf(y).FieldByName("B")
v.SetInt(33)

Update:

I'm not going to change x.b using y. I want to change y's field b alone.

Thanks for your help, but this is not a simple question about values and references.

kbridge4096
  • 901
  • 1
  • 11
  • 22
  • Now that it’s more clear since your update, it’s probably a duplicate of: https://stackoverflow.com/questions/6395076/using-reflect-how-do-you-set-the-value-of-a-struct-field – mgagnon Nov 01 '18 at 12:06
  • @mgagnon I don't think so. My question is about what actually happens when you assign a struct value to an empty interface (the struct value gets copied, and has no relationship with the original one) and how to access the copied value. I found this subtle and easy to be misinterpreted. – kbridge4096 Nov 01 '18 at 13:03
  • Ok, may be not exactly a duplicate but it shows how to do what you want using reflection. You need to reflect on the address of `y`, (by using `&y`) – mgagnon Nov 01 '18 at 14:16
  • @mgagnon Why using the address of y allows me setting one field in the value? – kbridge4096 Nov 02 '18 at 03:53
  • 1
    Because everything in Go is passed by value so if `reflect.ValueOf()` works on a copy of `y` it will not modify your original `y` variable. – mgagnon Nov 02 '18 at 17:20

2 Answers2

-1

You need to know the pointer and address first.Use the pointer can change the value deeply.If you want y to have the same value as x, they need to point to the same address.

zbwzbw
  • 1
  • 1
  • I don't want to change `x.b`, I want to change the member of the copied struct stored inside `y`. – kbridge4096 Nov 01 '18 at 05:30
  • @brk x has no reference to y at all. If you only have x in scope and want to modify a member of y, which isn't in your scope you won't be able to AND vice-versa, y has no reference to x etc. – dlamblin Nov 01 '18 at 05:48
  • @dlamblin but y has a reference/pointer to the copied struct. – kbridge4096 Nov 01 '18 at 05:54
-2

I found a solution:

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

It's really hack.

Can anybody provide a more natural/idiomatic one?


Summary:

  • assigning a concrete value to an empty interface copies the value
  • assigning an empty interface to another empty interface causes them to share the same underlying value
kbridge4096
  • 901
  • 1
  • 11
  • 22
  • There is no natural, idiomatic way to do it, because it's not supposed to be done. All assignments in go are copies, so the fact that this even works as it does with `y` and `z` sharing the same value is an implementation detail. – JimB Nov 01 '18 at 16:01
  • @JimB I agree with you. Maybe my question should be retitled "In golang, should the value held by an empty interface be considered immutable/read-only". But is there any specification which describes this behavior? – kbridge4096 Nov 02 '18 at 06:58
  • If you agree then I don't understand the question. Go interfaces are specified, and are operated on like any other value. It's "immutable" just like a string is immutable, and just like with a string, you can get around that immutability with some use of unsafe. Yes, you can bypass or break anything you want with unsafe, but that's doesn't mean it needs to be, or can reasonably be specified. – JimB Nov 02 '18 at 15:00
  • @JimB I thought they are something like variant variables which can store objects of any size and are not something like `const char *` in C++ which points to constant memory. – kbridge4096 Nov 03 '18 at 11:44
  • Your unsafe manipulations here indicate that you seem to understand the implementation, which is fairly simple, so I'm not sure what the confusion is about. Interfaces are currently implemented (regardless of method set) as an opaque, 2 word value -- one word to for the dynamic type, and one word for the pointer to the dynamic value. Why are you fighting the language to to access some unspecified behavior, which you could just use a pointer in the first place? – JimB Nov 05 '18 at 16:55
  • @JimB I understand the memory layout, not the whole implementation. What I want to figure out is the behavior such as what actually happens when you pass an empty interface value (copy the word pair or copy the underlying bytes, like things in C++.). I acknowledge that this question is a carefully designed experiment to prove something instead of a real-world problem. So people who suggest alternative methods may feel frustrated. Sorry for being paranoid, I just want to validate some of my ideas. – kbridge4096 Nov 06 '18 at 12:41
  • If that's the case, then your answer is really the only one. The caveat however is that this is unspecified behavior, and dependent on the particular implementation. The interfaces here share the same underlying value because assignment is a copy, so the interface value is what is copied, which still points to the same dynamic value, exactly the same as assigning a slice, or a string. You also keep mentioning "empty interface", but the method set has nothing to do with this. – JimB Nov 06 '18 at 13:36