This article explains the key differences between the `internal` and `private` visibility modifiers in Solidity and when to use each one.
In Solidity, managing access to your contract's functions and variables is essential for security and structure. You achieve this control using visibility specifiers: public
, internal
, external
, and private
. Each specifier dictates how and where a function or variable can be accessed, shaping the interaction points of your smart contract.
In Solidity, controlling access to your contract's functions and variables is key. You do this with these visibility specifiers: public
, internal
, external
, and private
.
Think of public
like an open door. Anyone, inside or outside the contract, can interact with it.
public uint256 myNumber;
external
is similar to public
, but it can only be called from outside the contract.
external function doSomething() {}
internal
is like a family secret. Only the contract itself and its children (contracts that inherit from it) can access it.
internal string secretMessage;
private
is the most restrictive. Only the contract itself can access it, not even its children.
private uint256 mySecretNumber;
If you don't specify visibility for a function, it defaults to internal
.
Understanding these visibility specifiers is crucial for writing secure and well-structured smart contracts.
This Solidity code demonstrates the different visibility levels for variables and functions within Solidity smart contracts. It showcases public, external, internal, and private visibility using a contract named VisibilityExample
. The example includes a derived contract to illustrate how visibility affects inheritance and a separate contract to demonstrate calling external functions. The code provides clear examples of how each visibility level controls access to members from within and outside the contract and its derived contracts.
pragma solidity ^0.8.0;
contract VisibilityExample {
// Public: Accessible from everywhere
uint256 public myNumber = 10;
// External: Only callable from outside the contract
external function changeNumberExternally(uint256 _newNumber) public {
myNumber = _newNumber;
}
// Internal: Accessible within this contract and derived contracts
internal string internalMessage = "This is a secret message!";
function getInternalMessage() public view returns (string memory) {
return internalMessage;
}
// Private: Only accessible within this contract
private uint256 privateNumber = 5;
function getPrivateNumberInternal() internal view returns (uint256) {
return privateNumber;
}
}
// Example of a derived contract
contract DerivedContract is VisibilityExample {
function getInternalMessageFromDerived() public view returns (string memory) {
// Can access internal members of the parent contract
return internalMessage;
}
// This would result in an error, as private members are not accessible
// function getPrivateNumberFromDerived() public view returns (uint256) {
// return privateNumber;
// }
}
contract CallerContract {
VisibilityExample visibilityExample = new VisibilityExample();
function callExternalFunction(uint256 _newNumber) public {
visibilityExample.changeNumberExternally(_newNumber);
}
}
Explanation:
VisibilityExample
contract:
myNumber
: A public variable, accessible from anywhere.changeNumberExternally
: An external function, callable only from other contracts.internalMessage
: An internal variable, accessible within VisibilityExample
and DerivedContract
.getInternalMessage
: A public function, allowing external access to the internal internalMessage
.privateNumber
: A private variable, accessible only within VisibilityExample
.getPrivateNumberInternal
: An internal function, demonstrating access to the private variable within the contract.DerivedContract
:
VisibilityExample
.getInternalMessageFromDerived
: Demonstrates access to the internal internalMessage
from the parent contract.getPrivateNumberFromDerived
shows that private members are inaccessible to derived contracts.CallerContract
:
VisibilityExample
.callExternalFunction
: Demonstrates calling the external function changeNumberExternally
of VisibilityExample
.This example illustrates how visibility specifiers control access to contract members, ensuring data protection and proper contract interaction.
General:
external
functions are slightly cheaper (gas-wise) than public
functions when interacting with them externally. This is because external
functions don't need to copy data to memory.internal
, it's good practice to explicitly declare the visibility for all functions and variables for better code readability and to avoid unintended consequences.Specifics:
public
:
public
, as any changes can have implications for external contracts relying on them.external
:
public
for external calls.internal
:
private
:
Inheritance:
private
members.private
member of its parent, it can still indirectly interact with it through internal
or public
functions provided by the parent contract.Best Practices:
private
) and only loosen it (internal
, external
, public
) if absolutely necessary.Specifier | Access Level | Description | Example |
---|---|---|---|
public |
Everyone | Accessible from inside and outside the contract. | public uint256 myNumber; |
external |
External only | Accessible only from outside the contract. | external function doSomething() {} |
internal |
Contract & Children | Accessible within the contract and its inheriting contracts. | internal string secretMessage; |
private |
Contract only | Accessible only within the contract itself. | private uint256 mySecretNumber; |
(none) | internal |
Default visibility if none is specified. |
Key Takeaway: Choosing the right visibility specifier is crucial for controlling access to your contract's data and functions, ultimately impacting its security and structure.
Mastering visibility specifiers is fundamental for Solidity developers. By strategically using public
, external
, internal
, and private
, you control the access points to your contract's functions and variables. This control is essential for building secure, well-structured, and gas-efficient smart contracts. Remember to follow best practices like starting with the most restrictive visibility and explicitly declaring it for all functions and variables. Clear documentation further enhances code readability and understanding, contributing to a robust and secure smart contract ecosystem.
public
, private
, internal
, external
.