Skip to content

Conversation

@keruch
Copy link

@keruch keruch commented Mar 21, 2025

Description

AccountAddress is used by value in most of the use cases. The most frequent use case is retrieving it from Account:

func (account *Account) AccountAddress() AccountAddress

Currently, all the AccountAddress methods are implemented using pointer receiver making the following constructions impossible:

_ = account.AccountAddress().String()
_ = account.AccountAddress().StringLong()
...

the only way to make it work is to do a copy and create a reference to it:

address := account.AccountAddress()
_ = address.String()

I suggest using value receiver where possible to make the UX better.

@keruch keruch requested review from a team and gregnazario as code owners March 21, 2025 18:11
@gregnazario
Copy link
Contributor

What's the difference in performance based on value receiver vs reference reciever?

I'm currently running through a list of lints, this will make it into the next release if possible after the one I'm working on right now.

@friedemannf
Copy link
Contributor

This is awesome thank you! I was about to put in a similar PR when I stumbled across this!

Just to add one more reason: for methods such as String() string it's always advisable to use value-receivers as this ensures that every instance will implement fmt.Stringer. Right now, when printing a non-pointer AccountAddress, the formatter won't apply:

	valueAddress := aptos.AccountZero
	pointerAddress := &aptos.AccountZero
	fmt.Println(valueAddress)
	fmt.Println(pointerAddress)

Will print:

[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
0x0

https://go.dev/play/p/LKSZeqeoNk_A

@keruch
Copy link
Author

keruch commented Mar 22, 2025

@gregnazario

What's the difference in performance based on value receiver vs reference receiver?

I do not have exact benchmarks, but given that AccountAddress is a fixed-size, immutable array of a basic type (bytes), I believe it would be more efficient to use a value receiver. A value receiver means that, rather than creating a pointer and dereferencing it on every method call, 32 bytes are copied onto the stack on each call, which is typically more efficient on modern architectures.

Anyway, I wouldn’t worry too much about performance; I think the difference here is insignificant.

References:
https://go.dev/wiki/CodeReviewComments#pass-values
https://go.dev/wiki/CodeReviewComments#receiver-type

@gregnazario
Copy link
Contributor

@gregnazario

What's the difference in performance based on value receiver vs reference receiver?

I do not have exact benchmarks, but given that AccountAddress is a fixed-size, immutable array of a basic type (bytes), I believe it would be more efficient to use a value receiver. A value receiver means that, rather than creating a pointer and dereferencing it on every method call, 32 bytes are copied onto the stack on each call, which is typically more efficient on modern architectures.

Anyway, I wouldn’t worry too much about performance; I think the difference here is insignificant.

References: https://go.dev/wiki/CodeReviewComments#pass-values https://go.dev/wiki/CodeReviewComments#receiver-type

Okay, it's weird since there seems to be 2 separate competing things:

  1. Don't make pointer and non-pointer functions (general lint)
  2. Don't make pointer functions when the item is small enough.

Probably good enough to have both, and just skip the lint. But, it is a pretty breaking change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants