Question: I thought that reading data on Ethereum was supposed to be gas free, however in the contract documentation (linked below) it states that for public arrays the automatically generated getter functions only return one element at a time to avoid large gas costs.

Isn't a getter function read-only? If so, why would it incur large gas costs?

See Getter Functions section in <https://docs.soliditylang.org/en/v0.5.3/contracts.html>

Answer: 
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()](https://www.trufflesuite.com/docs/truffle/reference/contract-abstractions#calling-getters) function, [Web3 call()](https://web3js.readthedocs.io/en/v1.3.4/web3-eth-contract.html#methods-mymethod-call) function, [JSON-RPC eth\_call](https://eth.wiki/json-rpc/API#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](https://docs.soliditylang.org/en/v0.8.4/contracts.html#view-functions) (read blockchain data, e.g. getters) or [pure](https://docs.soliditylang.org/en/v0.8.4/contracts.html#pure-functions) (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](https://docs.soliditylang.org/en/v0.5.3/contracts.html) 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));
    }
}

```
Read-only functions can incur gas costs when they are carried out during transactions, i.e. when wrapped inside a potentially state-changing function.

This is because, since this transaction requires verification, it will be executed by the networks nodes. This represents redundant work, eventually resulting in identical results (given the same initial state and the same transaction order).

To avoid this, standard gas-fees are applied to read-only functions invoked during state-changing transactions.

Source - <https://blog.b9lab.com/calls-vs-transactions-in-ethereum-smart-contracts-62d6b17d0bc2>