The Solidity Constructor: Runs Once, Then Never Again
What a constructor does, why it is the only function that cannot be called twice, how immutable and constant differ from it, and what it means when a contract has none.
TL;DR
- A constructor runs exactly once, during deployment, and then never again. There is no way to call it afterwards, by anyone, ever.
- It is where a contract's starting state gets written: who owns it, what its parameters are, which other contracts it points at.
- Because it runs at deployment,
msg.senderinside a constructor is the deployer, which is the standard way a contract learns who its owner is. - A contract with no constructor is legal and common. It simply starts with every state variable at its zero value.
- Constructor code is not part of the deployed contract. It runs, sets state, and is discarded.
What is a constructor in Solidity?
A constructor is a special function that the chain runs a single time, while the contract is being deployed, to set up its starting state, and which can never be called again once deployment finishes.
Here it is in a complete contract:
contract PiggyBank {
address public owner;
uint256 public goal;
constructor(uint256 _goal) {
owner = msg.sender;
goal = _goal;
}
Two lines of body, and they do the entire setup. Line one records who deployed the pig. Line two records the savings target that the deployer passed in.
Within the wider method of how to read a smart contract, the constructor is the first function you read after the state variables, because it is where those values came from. Call it the birth certificate. It is written at the moment the thing comes into existence, it records facts about that moment, and it is never rewritten.
Why does a constructor only run once?
Because it is not part of the deployed contract at all.
Deploying a contract means sending a transaction whose payload is the contract's creation code. The chain executes that creation code, the constructor runs as part of it, state gets written, and then the chain stores the runtime code at the new address. The constructor is in the creation code, not the runtime code. Once deployment finishes, there is physically nothing left to call.
This is why "can I call the constructor again?" has a harder no as its answer than most access questions in Solidity. It is not that a check refuses you. There is no function there.
One practical consequence for readers: the constructor is the only place in a contract where you will see setup logic that has no access control on it and does not need any. Nobody else was ever going to get the chance.
What is msg.sender inside a constructor?
The deployer. This is the detail that makes the whole pattern work.
msg.sender always means whoever is calling right now, in any function, at any moment. During deployment, the caller is the account that sent the deployment transaction. So this line:
owner = msg.sender;
records the deployer's address as the owner, permanently. If Ana deploys the PiggyBank, Ana is the owner from that instant, and because the constructor never runs again, and no other line in this contract assigns to owner, nobody else ever will be.
That last clause is the reading move worth internalising. To know whether a contract's owner can change, do not reason about it. Search the file for every assignment to the variable. In PiggyBank there is exactly one, inside the constructor, so the answer is no. In a contract with an transferOwnership function there are two, and the answer is yes. More on this in state variables in Solidity and msg.sender explained.
What if a contract has no constructor?
That is legal, common, and means something specific: every state variable starts at its zero value.
Solidity has no concept of an uninitialised variable. Every type has a default:
| Type | Default |
|---|---|
uint256 and other numbers | 0 |
bool | false |
address | address(0), the zero address |
string and bytes | empty |
So a contract with no constructor deploys with all numbers at zero, all flags false, and every address slot holding the zero address. That is not an error state, it is the defined starting point, and plenty of contracts are designed to start exactly there.
It does mean a specific question is always worth asking when you see no constructor: is there another function that performs setup instead? Upgradeable contracts in particular cannot use constructors in the usual way, and use an initialize function instead. That function is ordinary code, callable like any other, and whether it is protected from being called a second time is a real question you have to answer by reading it. A constructor never needed that protection. An initialiser does.
How do constant and immutable relate to this?
Both are ways of fixing a value, and the difference is when it gets fixed.
contract Example {
uint256 public constant MAX = 100; // fixed when compiled
address public immutable deployer; // fixed when deployed
constructor() {
deployer = msg.sender; // the only place immutable can be set
}
}
A constant is baked in at compile time, so it must be a literal value the compiler already knows. A value marked immutable is set once during deployment, which means the constructor is the only place it can be assigned, and then it is fixed forever.
Both are cheaper to read than ordinary state because neither lives in storage. And both give a reader a strong guarantee for free: a value declared constant or immutable cannot change, and you do not need to search the file to confirm it. The keyword is the proof.
Can a constructor take arguments?
Yes, and that is how the same contract source becomes many differently configured deployments.
constructor(uint256 _goal) {
_goal is a parameter. Whoever deploys the contract supplies its value in the deployment transaction, and different deployers can supply different values. One PiggyBank source, one deployed with a goal of 5 ETH, another with 100.
The leading underscore is a naming convention, not a language feature. It is widely used to distinguish a parameter from the state variable it will be assigned to, so _goal the input does not get confused with goal the stored value.
For a reader looking at a deployed contract, constructor arguments are worth chasing down, because they are the configuration that makes this instance different from every other instance of the same code. Block explorers show them, which is covered in how to read a contract on Etherscan.
Delete the word and ask what breaks
- Delete
owner = msg.sender;and the owner stays at the zero address forever. In PiggyBank, that means thesmashfunction can never pass its first check, and the ETH inside is stuck. - Rename
constructortofunction setup()and it stops being special. It becomes an ordinary function anyone can call, at any time, as often as they like. - Delete the whole constructor and the pig deploys with an owner of zero and a goal of zero. A goal of zero is immediately reached, which changes the contract's behaviour completely.
- Move
goal = _goal;into a separate function and the goal becomes changeable after deployment, which is a different contract with a different set of questions attached.
Related questions
Can you call a constructor after deployment? No. The constructor exists only in the contract's creation code, which runs during deployment and is not stored at the contract's address. After deployment there is no function to call.
What happens if a contract has no constructor? It deploys successfully and every state variable holds its type's zero value: numbers at zero, booleans false, addresses at the zero address. Many contracts are written this way deliberately.
Why do upgradeable contracts use initialize instead of a constructor? Because their state lives in a separate proxy contract, so constructor code running against the implementation would write state in the wrong place. They use an ordinary function instead, which means protecting it from being run twice becomes the author's responsibility.
What is the difference between constant and immutable?
constant is fixed at compile time and must be a literal the compiler can evaluate. immutable is fixed at deployment and can be assigned in the constructor, which lets it depend on deployment-time values such as msg.sender.
Can a constructor be payable?
Yes. Marking a constructor payable lets the deployment transaction send ETH along with it, so the contract can start life already holding a balance.
Why do parameters often start with an underscore? Purely convention, to avoid confusing a parameter with the state variable of the same name that it is about to be assigned to. Solidity does not treat the underscore specially in this position.
Where to go next
The constructor is the shortest and most consequential part of most contracts. It runs once, it sets the facts everything else depends on, and it is gone. When you open an unfamiliar contract, reading the constructor immediately after the state variables tells you where those values came from and, just as often, that they can never change.
The word doing the quiet work in that constructor is msg.sender, and it shows up in nearly every contract you will ever read: msg.sender in Solidity.
Tagged