It is written in many places how these things are. But what not easy to decide when to use which of them, as it needs a deeper understanding to choose.
So I want to note the quick heuristic methods to choose these.
Public vs. External
So if you know the function you create only allows for external calls, go for
external. It provides performance benefits and you will save on gas.
public means it can be
internal, the compiler need additional work for the public function. With the
external only, it allows arguments to be read directly from
calldata, saving the copying step.
Easy read here:
Memory vs. Calldata vs. Storage
calldatawhen you only need read-only data, avoiding the cost of allocating memory or storage.
memoryif you want your argument to be mutable.
storageif your argument will already exist in storage, to prevent copying something into storage over into memory unnecessarily.
To research more
I am still not sure why most of the OpenZeppelin libs choose to use
calldata, although they don't mutate them. For example: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L179
Top comments (1)
Found this one on why OpenZeppelin team uses
calldatais limited in functionalities when using with
internalfunction, so they go for