The following Go program produces 1,2,3,4 in followed by 5,5,5,5. I was expecting 1,2,3,4 in both cases. What am I doing wrong?
package main
import (
"fmt"
"math/big"
)
func primesLessThan(n *big.Int) (primes []big.Int) {
var one big.Int
one.SetInt64(1)
var i big.Int
i.SetInt64(1)
for i.Cmp(n) < 0 {
fmt.Println(i.String())
primes = append(primes, i)
i.Add(&i, &one)
}
return
}
func main() {
primes := primesLessThan(big.NewInt(5))
for _, p := range primes {
fmt.Println(p.String())
}
}
Update: the following code snippet illustrates the unexpected side effects of the shallow copy described in the responses. The output of the following snippet is 3, 3
one := big.NewInt(1)
two := big.NewInt(2)
one = two // Shallow copy. Question: how do I do a deep copy?
one.SetInt64(3) // Side-effect: also changes two
fmt.Println(one.String())
fmt.Println(two.String())
It's because of the way the object stores its internal data.
Take this example:
package main
import "fmt"
type foo struct {
value int
}
func bar() (r []foo) {
var f foo
for i := 0; i < 5; i++ {
f.value = i
r = append(r, f)
}
return
}
func main() {
for _, v := range bar() {
fmt.Println(v)
}
}
The output will be the expected
{0}
{1}
{2}
{3}
{4}
The issue in your example is that big.Int stores its value in a slice, and slices are pointers. So when a copy of a big.Int is created, the new copy contains a new pointer to the same slice in memory. A shallow copies is created rather than a deep copy.
See https://golang.org/src/math/big/int.go?s=388:468#L8 for how bit.Int
is declared, then see https://golang.org/src/math/big/nat.go#L25 for how nat
is declared.
Here is a solution that uses big.Int
package main
import (
"fmt"
"math/big"
)
func primesLessThan(n *big.Int) (primes []big.Int) {
var one big.Int
one.SetInt64(1)
var i big.Int
i.SetInt64(1)
for i.Cmp(n) < 0 {
var result big.Int
result.Set(&i)
fmt.Println(result.String())
primes = append(primes, result)
i.Add(&i, &one)
}
return
}
func main() {
primes := primesLessThan(big.NewInt(5))
for _, p := range primes {
fmt.Println(p.String())
}
}
What we see... Your primes []big.Int is acting like an array of pointers. By storing i in each slot of primes you are essentially storing the exact same pointer in each spot. That's why it's printing 5 four times.
Why we see this... Due to the internals of big.Int, each strict object of big.Int has an internal slice. Slices are implemented as pointers because they are really just fancy arrays. So, while you may have a big.Int object, the object has a pointer to a slice.
When copying a big.Int object you are copying this internal slice pointer.
@GarMan is right and I also agree it has to do with shallow vs. deep copies.
The @GarMan solution is fine but you can do simpler doing the string conversion at the append()
level for primes []string
:
primes = append(primes, i.String())