第四天:Solidity 核心语法一次过——mapping、struct、modifier 与错误处理
Hello World 跑通了,今天把 Solidity 最常用的四块核心语法一次讲透。这些内容是读任何一份开源合约的地基,花一天换后面读代码的速度,值。
mapping 和 struct:链上的键值对与结构体
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Votes {
struct Candidate {
string name;
uint256 voteCount;
}
mapping(address => uint256) public votedFor; // 谁投给了谁
mapping(uint256 => Candidate) public candidates; // 编号 => 候选人
uint256 public candidateCount;
function addCandidate(string calldata _name) external {
candidates[candidateCount++] = Candidate(_name, 0);
}
function vote(uint256 _id) external {
require(votedFor[msg.sender] == 0, "already voted");
votedFor[msg.sender] = _id;
candidates[_id].voteCount += 1;
}
}mapping(address => uint256) 等价于一个只能按 key 读写的哈希表:所有 key 默认存在且值为 0,所以没法遍历也没法”判空”,需要”是否投过票”这种布尔判断就得像上面那样借值本身编码(0 = 没投)。另外 msg.sender 是内置变量,永远等于”当前调用这笔交易的钱包地址”,权限判断全靠它。
数据位置:storage / memory / calldata
引用类型(string、数组、结构体)必须显式声明数据位置,这也是新手编译报错最集中的地方:
- storage:链上持久存储,读写都贵;状态变量默认在这里;
- memory:函数内的临时内存,函数结束即销毁;
- calldata:外部调用的参数只读区,最便宜,外部函数的引用类型参数尽量用它。
上面的 string calldata _name 就是这个用意:参数只读不改,用最省 gas 的位置。
modifier 与错误处理
contract Owned {
address public owner;
error NotOwner(address caller);
modifier onlyOwner() {
if (msg.sender != owner) {
revert NotOwner(msg.sender);
}
_;
}
constructor() {
owner = msg.sender;
}
function transferOwnership(address _new) external onlyOwner {
owner = _new;
}
}modifier 是函数的前置拦截器,_; 的位置就是被修饰函数体执行的位置——权限控制的标准写法。错误处理上,0.8 之后的最佳实践是自定义 error + revert,比 require("字符串") 省 gas,也比裸字符串好维护;调用失败时链下能拿到错误码和参数,前端报错可以做得非常精准。
今天的作业:把投票合约补一个”撤销投票”功能,要求用 modifier 限制只有已投票的人能调用,且撤销后候选人票数要减回去。写不出来就回去重读 mapping 那段——明天见。
读完了点一下,作者能得积分
赞赏
如果这篇内容帮到了你,不妨请作者喝杯咖啡
赞赏
如果这篇内容帮到了你,不妨请作者喝杯咖啡
选个金额
微信扫码扫一下,金额 ¥6
- 扫码后按所选金额支付即可
- 赞赏完全自愿,与积分、红包互不影响
发表评论