ARTICLE DETAIL

资讯详情

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

WTF-Solidity 教程精讲:Solidity 函数定义、可见性与 pure/view/payable 关键字完全指南

WTF-Solidity 教程精讲:Solidity 函数定义、可见性与 pure/view/payable 关键字完全指南 WTF-Solidity 教程精讲Solidity 函数定义、可见性与 pure/view/payable 关键字完全指南【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity本篇以 WTF-Solidity 教程第 3 讲《函数》为主体系统讲解 Solidity 函数的完整声明格式、四种可见性说明符public/private/internal/external、以及pure/view/payable等状态变更类关键字的区别与适用场景。读完本文你能读懂仓库中 Function.sol 的每一个函数声明理解为什么对状态变量的读取与改写权限会影响 gas 消耗并能在 Remix 或 Foundry 中独立编写、部署和调试自己的函数。Solidity 函数的声明格式教程原文先给出了 Solidity 函数的“总形式”这是理解后面所有细节的基础function function name([parameter types[, ...]]) {internal|external|public|private} [pure|view|payable] [virtual|override] [modifiers] [returns (return types)]{ function body }其中方括号[]内的部分为可写可不写的可选关键字。教程要求读者“从前往后逐个解释”这里完整继承其讲解脉络function声明函数的固定关键字编写函数必须以此开头。function name函数名。([parameter types[, ...]])圆括号内是函数的输入参数即写入函数的变量类型与名称。{internal|external|public|private}可见性说明符共 4 种见下一节。[pure|view|payable]决定函数权限/功能的关键字payable表示允许向合约转入 ETHpure和view见后文详解。[virtual|override]方法是否可以被重写或本身是否为重写方法。modifiers自定义修饰器可以有 0 个或多个教程在 11_Modifier 中会专门展开。[returns (return types)]函数返回值的类型与名称。function body函数体。下面逐点深入。四种可见性说明符函数可见性说明符共有 4 种语义如下public内部和外部均可见。private只能从本合约内部访问继承的合约也不能使用。external只能从合约外部访问但在合约内部可以通过this.f()调用f是函数名。internal只能从合约内部访问但继承的合约可以使用。这里有两条重要注意事项原文特别强调注意 1合约中定义的函数必须明确指定可见性它们没有默认值。漏写会直接编译报错。注意 2public/private/internal同样可用于修饰状态变量定义可参考 05_DataStorage/readme.md 的状态变量一节。public变量会自动生成同名的 getter 函数用于查询数值未标明可见性的状态变量默认为internal。源码印证仓库中的 FunctionTypes 合约仓库中 03_Function/Function.sol 与教程代码一一对应其状态变量声明本身就演示了“public 状态变量自动生成 getter”这一规则// SPDX-License-Identifier: MIT pragma solidity ^0.8.34; contract FunctionTypes{ uint256 public number 5; constructor() payable {} // ... }uint256 public number 5声明了一个公开状态变量。部署合约后Remix 侧边栏会自动出现一个名为number的查询按钮见下文 3-1 截图这正是 Solidity 编译器为public变量自动生成的 getter 函数无需手写任何查询代码。此外src/03_Function/Function.sol 是同一份合约在仓库src/目录下的镜像配合根目录 foundry.toml 中的solc 0.8.34配置可以直接用forge工具链编译运行适用前提是本地已安装 Foundry。到底什么是 pure 和 view这是本讲最核心、也是初学 Solidity 时最容易困惑的部分。pure和view在其他编程语言中没有对应关键字它们的出现源于以太坊的 gas 费用机制合约的状态变量存储在链上写入链上数据要支付昂贵的 gas fee如果一次计算不改变链上状态就不需要付 gas。pure和view函数都不改写链上状态因此用户直接在链下如钱包、Remix 侧边栏调用它们时不需要付 gas。这里有一个容易忽略的细节原文特别标注合约中非pure/view函数调用pure/view函数时仍需付 gas——“免费”的前提是调用方本身不发起状态变更交易。教程同时列出了以太坊中被视为“修改链上状态”的 8 种操作写入状态变量。释放事件emit event。创建其他合约。使用selfdestruct。通过调用发送以太币。调用任何未标记view或pure的函数。使用低级调用low-level calls。使用包含某些操作码的内联汇编。作者为帮助理解画了一个马里奥插图把合约中存储在链上的状态变量比作碧琪公主三种角色对应三种关键字——pure中文意思是“纯”可以理解为“纯打酱油的”pure函数既不能读取也不能写入链上状态变量就像小怪一样看不到也摸不到碧琪公主。view“看”可以理解为“看客”view函数能读取但不能写入状态变量。类似马里奥能看到碧琪公主但终究是看客不能入洞房。非pure也非view的普通函数既可以读取也可以写入状态变量类似马里奥里的boss可以对碧琪公主为所欲为。实战代码 1pure 与 view基础合约与默认函数按教程先在合约里定义状态变量number初始化为 5contract FunctionTypes{ uint256 public number 5; }再定义一个add()函数每次调用让number增加 1// 默认function function add() external{ number number 1; }这就是一个最“默认”的函数external可见、不带pure/view/payable因此它可以读写状态变量——调用它就会修改链上数据用户调用需要付 gas。pure纯纯牛马如果把add()标记为pure例如function add() external pure编译器会报错——因为pure不配读取合约里的状态变量更不配改写。那pure函数能做什么教程给出的做法是给函数传递一个参数_number返回_number 1。这个操作既不读取也不写入任何状态变量// pure: 纯纯牛马 function addPure(uint256 _number) external pure returns(uint256 new_number){ new_number _number 1; }从截图可以看到调用addPure时输入参数_number 1decoded output 显示0: uint256 new_number 2即返回 1 1 2。注意执行记录中显示 execution cost 仅约 22000 gas 且标注 “Cost only applies when called by a contract”印证了 pure 函数在独立外部调用时不计 gas 费。view看客如果add()被标记为view即function add() external view同样会报错因为view能读取但不能改写状态变量。教程的解法是改写函数只读取number把结果存到新的局部变量中返回而不写回状态变量// view: 看客 function addView() external view returns(uint256 new_number) { new_number number 1; }截图中addView()无参数返回0: uint256 new_number 6——它读取了链上状态变量number此时为 5返回 5 1 6而链上的number本身保持不变。这正是 view 语义的典型体现可读链上状态、不可写。实战代码 2internal 对比 external教程第二部分演示可见性差异定义一个internal的minus()函数每次调用使number减少 1// internal: 内部函数 function minus() internal { number number - 1; } // 合约内的函数可以调用内部函数 function minusCall() external { minus(); }由于internal函数只能由合约内部调用外部无法直接触发minus()所以教程额外定义了一个external的minusCall()作为“外壳”通过它间接调用内部的minus()。截图清楚地印证了这一规则合约部署后Remix 侧边栏自动生成的按钮只有add、minusCall、minusPayable、addPure、addView和numbergetter没有minus按钮——internal的minus()无法被链上外部调用只能经由minusCall()间接执行。这也是为什么合约中大量“辅助逻辑”会被写成internal既能让外部入口受控又能被多个 public 函数复用。实战代码 3payable第三部分引入payable关键字// payable: 递钱能给合约支付eth的函数 function minusPayable() external payable returns(uint256 balance) { minus(); balance address(this).balance; }minusPayable()是external payable的函数它内部间接调用minus()并返回合约当前的 ETH 余额。其中this关键字让我们引用当前合约地址address(this).balance即该合约的 ETH 余额。由于函数带payable调用minusPayable()时可以在消息msg中附带转账例如转入 1 个 ETH。教程中的 Remix 演示结果显示调用minusPayable并附带 1 ETH 后返回值balance为 1 ETH即1000000000000000000wei说明转账成功落袋合约余额变为 1 ETH。需要注意的隐含规则只有构造函数或标记了payable的函数才能接收 ETH。如果调用方给一个非payable函数附带 ETH交易会回滚。这也是 03_Function/Function.sol 中构造函数写成constructor() payable {}的原因——部署时即允许携带 ETH。总结本讲完整覆盖了 WTF-Solidity 第 3 讲《函数》的全部内容并对照仓库源码做了印证函数声明格式function 名字(参数) {可见性} [pure|view|payable] [virtual|override] [修饰器] [returns(返回)] { 函数体 }其中可见性必须显式指定没有默认值。四种可见性public内外均可见private仅本合约可见连继承合约也不行external仅外部可见内部可用this.f()调用internal本合约及继承合约可见。pure/view/payablepure函数既不能读也不能写状态变量view函数可读不可写二者在用户直接调用时免 gaspayable允许调用方随函数调用向合约转入 ETH配合address(this).balance可以查询合约余额。状态变量public状态变量自动生成同名 getter 函数未标注可见性的状态变量默认internal。仓库中的完整可运行代码见 03_Function/Function.solsrc/03_Function/Function.sol 为 Foundry 工作区下的镜像英文对照版见 Languages/en/03_Function_en/readme.md。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表