What's the rationale for methods returning pointers in the ecc packages?
samlaf opened this issue · 3 comments
I'm curious in general about the rationale behind the design of the APIs in the ecc package. Has this been discussed anywhere?
More specifically, most methods are defined similarly to this Add method on G1Affine points:
func (p *G1Affine) Add(a, b *G1Affine) *G1Affine
that is, they take a pointer receiver, pointer arguments, and return a pointer. Is there a reason why they don't instead take the structs directly? I've just been bitten by a side effect bug due to this. Rust uses borrow checker to prevent such side effects. Functional programming generally favors immutable data structures precisely to prevent this kind of error. This is why I was surprised to see this API design.
Also unclear to me if this was written for performance reasons, since sometimes having small structs (like these curve points) on the stack can be faster. The zen of go has a principle "If you think it’s slow, prove it with a benchmark". Are there benchmarks comparing these two approaches somewhere?
Related question. The zen of go also mentions “APIs should be easy to use and hard to misuse.” The design of the Add function above (and related methods) are very confusing to me. Why do they take a receiver object if it is only used to return the value? Compare this to the internal add method
func (p *g1JacExtended) add(q *g1JacExtended) *g1JacExtended
which only takes a single argument, to be added to the receiver. Why does the Add() method take both a and b. Why not just a single argument to be added to the receiver?
Coworker made me realize it's probably based off of the big.Int API. They justify their API for eg by
(By always passing in a result value via the receiver, memory use can be much better controlled. Instead of having to allocate new memory for each result, an operation can reuse the space allocated for the result value, and overwrite that value with the new result in the process.)
Would be useful to add similar mentions in the doc, or at least link to the big.Int API in justification.
Hi @samlaf , yes it's the exact reason, the goal was to mimic the big.Int api.