0

I have come across a bit of unexplained behavior of go's big.Int when pointing an instance of one big.Int to another. I am knowledgeable that in order to set a value of a bit.Int's instance to another, one must use the Int.SetXXX setters, because they actually cause the underlying abs slice in big.Int to be copied to a newly allocated array. However, putting that aside for a moment, I'd like to know why the following behavior occurs.

Consider the following:

Wrong example (underlying value mutates):

func main() {
    v1p := big.NewInt(1)
    v2p := big.NewInt(2)

    v1 := *v1p
    v2 := *v2p

    v2 = v1

    v1.SetInt64(3)

    fmt.Println(v1.Int64(), v2.Int64())
}

(run here: https://play.golang.org/p/WxAbmGdKG9b)

Correct example (value does not mutate):

func main() {
    v1p := big.NewInt(1)
    v2p := big.NewInt(2)

    v1 := *v1p
    v2 := *v2p

    v2.Set(v1p)

    v1.SetInt64(3)

    fmt.Println(v1.Int64(), v2.Int64())
}

(run here: https://play.golang.org/p/16qsGhwHIWf)

If I understand correctly, the following should essentially demonstrate what happens in the wrong example:

func main() {
    var a, b *int // analogous to the 2 big.Int pointers returned
    c, d := 3, 3
    a = &c // we set the pointers to point to something we can then dereference
    b = &d

    e := *a // e and f should now point to the values pointed to by the pointers
    f := *b

    // the rest is self-explanatory
    e = f

    c = 5
    d = 4

    fmt.Println(a, b, c, d, e, f)
}

(run here: https://play.golang.org/p/cx76bnmJhG7)

My only assumption is that somehow when copying the struct content onto v2 in the Wrong example, what happens is that abs slice does not get deep-copied but that the storage that it references is actually the same storage that the slice in v1 points to.

Is this really what happens? Is this the expected behavior according to the language spec too?

acud
  • 89
  • 6
  • In the first example, both v1 and v2 will reference v1p after `v2 = v1`, so you're not mutating `v2p`, you're printing `v1p` twice. (this is 100% guesswork since I know very little go). See https://play.golang.org/p/GgdhXXNdB85 – Lasse V. Karlsen Mar 23 '20 at 15:42
  • 3
    You should never be dereferencing a `*big.Int`, so this should never be a problem. a `big.Int` contains an internal slice (which implies a pointer) which is what you're copying. – JimB Mar 23 '20 at 15:44
  • 2
    Assigning `v1` to `v2` copies the struct value, which includes the `nat` field which is a slice. Slices are struct-like headers containing a pointer to a backing array. Copying a slice just copies the header, including the pointer, and the copy will point to the same backing array. See details: [Are golang slices passed by value?](https://stackoverflow.com/questions/39993688/are-golang-slices-passed-by-value/39993797#39993797) and [How to inspect slice header?](https://stackoverflow.com/questions/54195834/how-to-inspect-slice-header/54196005#54196005) – icza Mar 23 '20 at 15:45
  • 2
    As you properly guessed no copy in Go is a deep copy. Any struct containing slices, pointer, channels or closures cannot be deep-copied (unless it provides a function/method to do so). – Volker Mar 23 '20 at 15:46
  • 2
    Going forward, what happens may be implementation dependant. Since you have now 2 struct values (not pointers), calling a method on one that mutates it, the 2 struct values may be inconsistent. The one you call the method, may update the slice elements and other struct fields. Other changed struct fields will not be reflected on the other struct, and slice element changes may or may not be (e.g. if the slice gets extended with `append()`, that creates a new slice header but the other struct will not see this; but if current elements get changed that will be). – icza Mar 23 '20 at 15:47

1 Answers1

0

As pointed out by icza and Volker, since when dereferencing the big.Int pointer the actual value slice header struct is copied, pointing to the same underlying value, the resulting behavior is that the same underlying array gets referenced from multiple slices, resulting in one altering the other.

acud
  • 89
  • 6