ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

WTF Solidity 第 7 讲:mapping 映射类型——Solidity 中的哈希表详解

WTF Solidity 第 7 讲:mapping 映射类型——Solidity 中的哈希表详解 WTF Solidity 第 7 讲mapping 映射类型——Solidity 中的哈希表详解【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity本篇为 WTF-Solidity 极简入门教程第 7 讲英文版聚焦 Solidity 中的键值对数据结构mapping映射/哈希表。文章从声明语法、四条使用规则、三条底层原理出发结合本仓库 Mapping.sol 源码与 ERC20/ERC1155 中的真实嵌套映射用法讲解如何通过Key查询Value例如通过用户id查询钱包地址并在 Remix 上完成部署、查询初始值与写入键值对的完整验证。学完本讲你将掌握mapping的声明、读写、可见性与存储约束并能读懂真实合约中的映射应用模式。什么是映射Mappingmapping是 Solidity 中存储键值对key-value pair的数据结构本质上是一张哈希表用户可以通过键Key来查询对应的值Value。例如可以通过一个人的id来查询他的钱包地址。声明映射的格式为mapping(_KeyType _ValueType)其中_KeyType和_ValueType分别是Key和Value的变量类型。本仓库示例合约 Mapping.sol 中的两个典型声明mapping(uint address) public idToAddress; // id maps to address mapping(address address) public swapPair; // Mapping of token pairs, from address to addressidToAddress以uint类型的id为键映射到address类型的钱包地址是最常见的索引查询场景swapPair从交易对代币地址到兑换目标地址的映射address address的形态常用于 DEX 路由或兑换逻辑中记录配对关系。映射的四大使用规则规则 1_KeyType必须是内置类型_ValueType可以是任意类型映射的_KeyType只能选择 Solidity 内置的值类型例如uint、address、bool、bytes、string、合约类型和枚举类型等不能使用自定义的结构体struct而_ValueType则可以是任意类型包括自定义结构体。下面的写法会直接报编译错误因为_KeyType使用了自定义结构体Student// Define a struct struct Student{ uint256 id; uint256 score; } mapping(Student uint) public testVar; // Error: KeyType cannot be a custom struct这一限制的根本原因在于mapping的Key会被哈希keccak256后用于计算存储位置而结构体没有明确的哈希编码规则无法可靠地作为查找键。该反例在 Mapping.sol 中以注释形式保留方便读者对照理解。规则 2存储位置必须是storage映射的存储位置必须为storage因此它可以作为合约的状态变量、函数内部的storage变量以及 library 函数的参数使用但不能用于public函数的参数或返回结果中因为mapping记录的是一种关系key-value pair无法在函数调用间整体拷贝或序列化传递。实际开发中绝大多数mapping都直接声明为合约的状态变量本讲示例即如此这是最安全的用法。规则 3public映射自动生成getter如果映射声明为publicSolidity 会自动为其创建一个getter函数外部调用者可以通过Key查询对应的Value。例如声明mapping(uint address) public idToAddress;后Remix 的部署面板会自动出现idToAddress按钮输入_Key即可读到对应地址。这一点会在下文Remix 验证环节实际操作验证。规则 4新增键值对的语法为_Var[_Key] _Value给映射新增或更新键值对的语法为_Var[_Key] _Value;其中_Var是映射变量名_Key和_Value对应要写入的键值对。本讲合约中的写入函数function writeMap (uint _Key, address _Value) public { idToAddress[_Key] _Value; }调用writeMap(1, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4)后idToAddress[1]就被写入了该地址重复写入同一Key会覆盖旧值。映射的三大底层原理理解原理有助于预测mapping的行为、规避常见坑点也是阅读合约存储布局的基础。原理 1不存储键信息和长度信息映射不存储任何Key的资讯也没有length资讯。因此你无法遍历一个mapping也无法获知它有多少个键。这与数组array有本质区别——数组可以for循环遍历并读取.length而映射只能通过已知的Key做定点查询。如果需要遍历键集合必须自行用数组额外维护一份键列表。原理 2通过keccak256(h(key) . slot)计算存取位置映射对每个键值对使用keccak256对键的哈希与槽位进行拼接计算得出该Value在存储中的实际位置。这意味着键值对之间没有线性排列关系存储位置由哈希结果决定增加新键不会影响其他键的存储位置查询是 O(1) 常数时间操作。这一布局在 WTF-Solidity 内部规则专题中有更深入的讲解本文不再展开感兴趣的读者可关注仓库中关于存储布局的进阶内容。原理 3未赋值的键初始值为该类型的默认值由于 Ethereum 将存储中所有未使用的空间定义为 0因此所有未被赋值的Key读取到的初始值都是其值类型的默认值例如uint的默认值为0address的默认值为0x0零地址bool的默认值为false。这一点在下面 Remix 验证中会直观看到新部署的合约里idToAddress[0]读出来就是0x0000000000000000000000000000000000000000。在 Remix 上验证以 Mapping.sol 为例完整示例代码见 Languages/en/07_Mapping_en/Mapping.sol中文版位于 07_Mapping/Mapping.sol 与 src/07_Mapping/Mapping.sol。建议在 Remix 中新建文件并粘贴代码编译器版本选择^0.8.34对应的环境本文示例基于 0.8.34。第一步部署合约在 Remix 的 Solidity 编译器中编译并部署Mapping合约。部署成功后合约面板会列出两个public映射对应的查询按钮idToAddress与swapPair以及写入函数writeMap。第二步查询初始值在idToAddress输入框填入_Key例如0并点击调用会返回零地址0x0000000000000000000000000000000000000000。这正是原理 3 的直接体现所有未赋值的键初始值都是值类型的默认值。第三步写入键值对并再次查询在writeMap输入框中传入_Key与_Value例如1和一个测试地址发送交易完成写入随后再次调用idToAddress(1)即可读到刚刚写入的地址证明键值对已生效。纵深拓展嵌套映射与真实合约中的应用本讲教程只覆盖了单层映射但真实合约中大量使用嵌套映射mapping 的值仍然是 mapping其本质是键里套键的多级索引。理解下面这些仓库内的真实案例能让映射知识直接落地到主流标准合约中。ERC20 中的嵌套映射余额与授权在本仓库 31_ERC20/ERC20.sol 中代币余额和授权额度都直接用映射实现mapping(address uint256) public override balanceOf; mapping(address mapping(address uint256)) public override allowance;balanceOf每个地址对应一个代币余额是address uint256的经典应用allowanceallowance[owner][spender]表示owner授权给spender的代币额度是嵌套映射的标准写法——先按owner定位再按spender定位。对应地transfer中更新余额的写法正是规则 4 的语法balanceOf[msg.sender] - amount;、balanceOf[recipient] amount;approve中则是allowance[msg.sender][spender] amount;。ERC1155 中的嵌套映射多代币持仓与批量授权在 40_ERC1155/ERC1155.sol 中多代币multi-token的持仓与授权同样基于嵌套映射mapping(uint256 mapping(address uint256)) private _balances; // 代币种类id 账户 余额 mapping(address mapping(address bool)) private _operatorApprovals; // 发起方 授权方 是否授权_balances[id][account]先按代币id定位再按账户定位其持仓量_operatorApprovals[account][operator]布尔值嵌套映射记录是否授权读取时_operatorApprovals[account][operator]默认返回false恰好体现了原理 3 的默认值行为。ERC1155 的safeTransferFrom中_balances[id][from] fromBalance - amount;、_balances[id][to] amount;等操作也都是映射读写语法的直接运用。总结本讲介绍了 Solidity 中哈希表——映射Mapping类型的完整用法声明格式mapping(_KeyType _ValueType)、四条规则键类型限制、存储位置必须为storage、public自动生成 getter、_Var[_Key] _Value的读写语法以及三条底层原理不存键与长度、keccak256计算存储位置、未赋值键返回默认值。结合本仓库的 Mapping.sol、ERC20.sol 与 ERC1155.sol 可以看到mapping是 Solidity 中最常用的数据容器之一从代币余额到授权额度、从多代币持仓到权限管理几乎所有状态型业务都依赖它。至此我们已经学完了 Solidity 中所有常用变量种类值类型、数组、结构体与映射下一阶段即可进入控制流if-else、while等的学习用这些数据结构编写真正的业务逻辑。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表