Compiler Rejects Functions Without Visibility (Solidity 0.5.0+): Public Getter or Explicit Accessors?
0 reputation · 25 Sept 2026, 19:10 UTC
0 reputation · 25 Sept 2026, 19:10 UTC
Since Solidity 0.5.0, the compiler rejects any function declared without an explicit visibility specifier. Before that release, a missing keyword silently defaulted to public — a classic route to accidental external access. Function visibility is now mandatory, but the neighboring state-variable decision remains open.
State variables default to internal, yet marking one public makes the compiler emit a getter that anyone can call. That getter has a fixed shape:
Consider a contract holding a struct of user records that includes a balance mapping. Declaring it public would expose a free getter that skips the mapping member; keeping it internal means writing explicit getters, which can enforce conditions the generated one cannot. Private does not hide storage from off-chain readers either, so neither option is confidential.
Should such a struct be declared public with its partial, unrestricted getter accepted as-is, or kept internal with hand-written accessors that can check caller and contract state? And is the generated-getter-versus-explicit-function tradeoff worth a gas and bytecode-size comparison before the ABI is frozen?
30775 reputation · 25 Sept 2026, 22:52 UTC
When a struct contains a mapping (or an array) and you need to expose its data, the safest and most flexible approach is to keep the variable internal and write explicit getter functions that can enforce require checks on msg.sender or contract state. A public declaration will generate a getter that returns every non‑mapping, non‑array member but cannot be overridden, guarded, or reshaped.
public is limitedview and external; you cannot add onlyOwner or similar modifiers.name() for simple types, name(key) for mappings, name(index) for arrays). Replacing the variable with an internal one requires re‑implementing a matching function to preserve ABI compatibility.internal + manual getters is preferable when you need controlThe compiler‑generated getter is compact and incurs only storage reads. A manual getter adds a few extra instructions for the require checks and for packing/unpacking the return data. In most cases the gas difference is modest (<10 % for a simple check) and the bytecode size impact is small. However, if the contract is deployed on a chain with very high deployment costs or you have many such getters, profiling on the target fork is advisable.
internal for the struct and write a function getUser(address user) external view returns (uint balance, uint lastUpdated) (or similar) that includes your access logic.external.public. Use evm_snapshot and evm_revert to isolate the cost.Could you confirm the compiler version and optimizer settings you will use for deployment? Those details influence whether a public getter can be overridden and affect the exact gas numbers.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.