Solidity 中函数的形式:
function <function name>([parameter types[, ...]]) {internal|external|public|private} [pure|view|payable] [virtual|override] [<modifiers>]
[returns (<return types>)]{ <function body> }
function:声明函数时的固定用法。要编写函数,就需要以function关键字开头。<function name>:函数名。([parameter types[, ...]]):圆括号内写入函数的参数,即输入到函数的变量类型和名称。{internal|external|public|private}:函数可见性说明符,共有4种。
-
public:内部和外部均可见。 -
private:只能从本合约内部访问,继承的合约也不能使用。 -
external:只能从合约外部访问(但内部可以通过this.f()来调用,f是函数名)。 -
internal: 只能从合约内部访问,继承的合约可以用。注意 1:合约中定义的函数需要明确指定可见性,它们没有默认值。注意 2:
public|private|internal也可用于修饰状态变量。public变量会自动生成同名的getter函数,用于查询数值。未标明可见性类型的状态变量,默认为internal。
[pure|view|payable]:决定函数权限/功能的关键字。payable(可支付的)很好理解,带着它的函数,运行的时候可以给合约转入 ETH。pure和view的介绍见下一节。[virtual|override]: 方法是否可以被重写,或者是否是重写方法。virtual用在父合约上,标识的方法可以被子合约重写。override用在自合约上,表名方法重写了父合约的方法。<modifiers>: 自定义的修饰器,可以有0个或多个修饰器。[returns ()]:函数返回的变量类型和名称。<function body>: 函数体。
到底什么是 Pure 和View?
以太坊交易需要支付气费(gas fee),合约的状态变量存储在链上,gas fee很贵,如果计算不改变链上状态就不需要支付gas,包含Pure和View关键字的函数是不改变链上状态的,因此用户直接调用它们是不需要付 gas 的。
被视为改写链上状态的语句
- 写入状态变量
- 释放事件
- 创建其他合约
- 使用
selfdestruct. - 通过调用发送以太币
- 调用任何未标记
view或pure的函数。 - 使用低级调用(low-level calls)。
- 使用包含某些操作码的内联汇编。
代码
-
pure 和 view
我们在合约里定义一个状态变量number,初始化为 5。// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; contract FunctionTypes{ uint256 public number = 5; }定义一个 add() 函数,每次调用会让 number 增加 1。
// 默认function function add() external { number = number + 1; }如果
add()函数被标记为pure,比如function add() external pure,就会报错。因为pure是不配读取合约里的状态变量的,更不配改写。那pure函数能做些什么?举个例子,你可以给函数传递一个参数_number,然后让他返回_number + 1,这个操作不会读取或写入状态变量。function addPure(uint256 _number) external pure returns(uint256 new_number){ new_number = _number + 1; }如果
add()函数被标记为view,比如function add() external view,也会报错。因为view能读取,但不能够改写状态变量。我们可以稍微改写下函数,读取但是不改写number,返回一个新的变量。function addView() external view returns(uint256 new_number) { new_number = number + 1; } -
internal v.s. external
// internal: 内部函数 function minus() internal { number = number - 1; } // 合约内的函数可以调用内部函数 function minusCall() external { minus(); }我们定义一个
internal的minus()函数,每次调用使得number变量减少 1。由于internal函数只能由合约内部调用,我们必须再定义一个external的minusCall()函数,通过它间接调用内部的minus()函数。 -
payable
// payable: 递钱,能给合约支付eth的函数
function minusPayable() external payable returns(uint256 balance) {
minus();
balance = address(this).balance;
}
我们定义一个 external payable 的 minusPayable() 函数,间接的调用 minus(),并且返回合约里的 ETH 余额(this 关键字可以让我们引用合约地址)。我们可以在调用 minusPayable() 时往合约里转入1个 ETH。
总结
view 函数可以读取状态变量,但不能改写;pure 函数既不能读取也不能改写状态变量。