I'm trying to understand the design decisions behind the big int api.
For example to add two big ints you have to:
a := big.NewInt(10)
b := big.NewInt(20)
c := big.NewInt(0)
d := c.Add(a,b)
where d is the same as c at the end. The initial zero does not matter a bit.
Why not just:
a := big.NewInt(10)
b := big.NewInt(20)
c := big.Add(a,b)
Or better yet:
a := big.NewInt(10)
b := big.NewInt(20)
c := a.Add(b)
Is there any reason they chose to do it this way? I find it little confusing and have to look it up whenever I use it.
Add
is a method changing the receiver.
So just do
c := big.NewInt(0).Add(a,b)
or
var c big.Int
c.Add(a,b)
The fact that Add returns the receiver is useful for function chaining but you don't need to use the returned value.
Now suppose a moment that we wouldn't have a bigInt as receiver (c := big.Add(a,b)
) or that the receiver wouldn't be modified (c := a.Add(b)
). In both cases a big Int would have to be allocated just for the operation and returned (as a pointer). This would be wasteful in case you yet have a big Int allocated and ready. The integer that is computed isn't just a simple one or two words struct, it can be big. So it's better to allow the use of a predefined var, especially as you often would use your big integer in the middle of a computation loop.
c := big.Add(a,b) // wasteful because doesn't allow the use of a preexisting big int
c := a.Add(b) // either modifies a (which would force you to copy it each time if you want to keep it) or is wasteful like the last one
I would add to Denys' answer that if you consider the alternative api, which could support chaining as follows:
x.Add(y).Add(z).Mul(v)
Quickly the question is - does this obey normal operator ordering?
x+y+z*v = x+y+(z*v)
but the first chain would result in (x+y+z)*v (in go, but possibly not in another language) - hence care is needed.
This:
r = r.AddP(x, y.Add(y, z.Mul(z, v)))
is somewhat uglier, i agree, but it does forces an explicit order, and it also gives us the chance to leave the operands unchanged, with no extra allocation (as mentioned by Denys). e.g. (notice r is the receiver every time):
r = r.Add(x, r.Add(y, r.MulInt(z, v)))
here only the result value (r) is changed, x,y,z are unchanged - to do this in the first style API you need an allocation each time. So in that case you either mutate the operands, or you allocate, in the big.Int api you have the option of either.
Btw, the following would be the equivalent to the first chaining...
r = r.Mul(r.AddP(x, r.Add(y, z)), v)
Which actually looks a bit more explicitly like (x+y+z)*v
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With