There are two main ways to interact with a smart contract.
- Read-only
call is gas free.
- Transaction (can generate state changes - storage change, event logs, ...) requires gas fees.
From the perspective of an off-chain client app, you can invoke a call using Truffle call() function, Web3 call() function, JSON-RPC eth_call method, and few other ways.
From the perspective of a Solidity contract, read-only functions that don't make state changes, should be marked as view (read blockchain data, e.g. getters) or pure (dont't read blockchain data, e.g. math helpers).
If you have a public state variable of array type, then you can only retrieve single elements of the array via the generated getter function. This mechanism exists to avoid high gas costs when returning an entire array. You can use arguments to specify which individual element to return, for example data(0). If you want to return an entire array in one call, then you need to write a function
Source: Docs linked from your question
Isn't a getter function read-only? If so, why would it incur large gas costs?
It is read-only. But it doesn't mean that it can be only accessed using the read-only call.
When you send a transaction to Contract A which needs to read data from Contract B, it makes an internal transaction. Example:
Contract A deployed on 0x123
pragma solidity ^0.8;
interface B {
// this is the generated getter function that the docs mention
function data(uint256 index) external returns (address);
}
contract A {
// executing this function requires a transaction
function getContractBFirstItem() external returns (address) {
B memory b = B(address(0x456));
// this creates an internal transaction (not a read-only call)
address firstItem = b.data(0);
return firstItem;
}
}
Contract B deployed on 0x456
pragma solidity ^0.8;
contract B {
// the getter function is generated from this public property
address[] public data;
constructor() {
// so that fhe first item exists and we can test it
data.push(address(0x789));
}
}