WTF Solidity极简入门:3. 函数类型

我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。

推特@0xAA_Science

WTF Academy社群: 官网 wtf.academy | discord | 微信群申请

所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity

Solidity中的函数

solidity官方文档里把函数归到数值类型,但我觉得差别很大,所以单独分一类。我们先看一下solidity中函数的形式:

function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]

看着些复杂,咱们从前往后一个一个看(方括号中的是可写可不写的关键字):

1. function:声明函数时的固定用法,想写函数,就要以function关键字开头。

2. ():圆括号里写函数的参数,也就是要输入到函数的变量类型和名字。

3. {internal|external|public|private} :函数可见性说明符,一共4种。没标明函数类型的,默认internal。

  • public: 内部外部均可见,并且自动给stoage变量生成 getter 函数

  • private: 只能从本合约内部访问,继承的合约也不能用。

  • external: 只能从合约外部访问(但是可以用this.f()来调用,f是函数名)

  • internal: 只能从合约内部访问,继承的合约可以用。

4. [pure|view|payable]:决定函数权限/功能的关键字。payable很好理解,带着它的函数,运行的时候可以给合约转入ETH。pure和view的介绍见下一节。

5. [returns ()]:函数返回的变量类型和名称。

到底什么是Pure和View?

我刚开始学solidity的时候,一直不理解pure跟view关键字,因为别的语言没有类似的关键字。solidity加入这两个关键字,我认为是因为gas fee。合约的状态变量存储在链上,gas fee很贵,如果不改写这些变量,就不用付gas。调用pure跟view的函数是不需要付gas的。

我画了一个马里奥插画,帮助大家理解。在插画里,我把合约中的状态变量(存储在链上)比作碧池公主,三种不同的角色代表不同的关键字。

WTH is pure and view in solidity?
WTH is pure and view in solidity?

pure,中文意思是“纯”,在solidity里理解为“纯纯牛马”。包含pure关键字的函数,不能读取也不能写入存储在链上的状态变量。就像小怪一样,看不到也摸不到碧池公主。

view,“看”,在solidity里理解为“看客”。包含view关键字的函数,能读取但也不能写入状态变量。类似马里奥,能看到碧池,但终究是看客,不能入洞房。

不写pure也不写view,函数既可以读取也可以写入状态变量。类似马里奥里的boss,可以对碧池公主为所欲为🐶。

代码

1. pure v.s. view

我们在合约里定义一个状态变量 number = 5。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract FunctionTypes{
    uint256 public number = 5;

定义一个add() function,每次调用,输出 number + 1。

    // 默认
    function add() external{
        number = number + 1;
    }

如果add()包含了pure关键字,例如 function add() pure external,就会报错。因为pure(纯纯牛马)是不配读取合约里的状态变量的,更不配改写。那pure函数能做些什么?举个例子,你可以给函数传递一个参数 _number,然后让他返回 _number+1。

    // pure: 纯纯牛马
    function addPure(uint256 _number) external pure returns(uint256 new_number){
        new_number = _number+1;
    }

如果add()包含view,比如function add() view external,也会报错。因为view能读取,但不能够改写状态变量。可以稍微改写下方程,让他不改写 _number,而是返回一个新的变量。

    // view: 看客
    function addView() external view returns(uint256 new_number) {
        new_number = number + 1;
    }

2. Internal v.s. External

    // internal: 内部
    function minus() internal {
        number = number - 1;
    }

    // 合约内的函数可以调用内部函数
    function minusCall() external {
        minus();
    }

我们定义一个internal的minus()函数,每次调用使得number变量减1。由于是internal,只能由合约内部调用。我们再定义一个external的minusCall()函数,调用minus()。这样,人们就能通过调用minusCall()来间接调用internal的minus()。

3. Payable

    // payable: 递钱,能给合约支付eth的函数
    function minusPayable() external payable returns(uint256 balance) {
        minus();    
        balance = address(this).balance;
    }

我们定义一个external payable的minusPayable()函数,间接的调用minus(),并且返回合约里的ETH余额(this关键字可以让我们引用合约地址)。我们调用minusPayable()时,往合约里转入1个ETH。

我们可以在返回的信息中看到,合约的余额是1 ETH。

注意:如果想在部署合约的时候往合约里转账,则需要给合约的构造函数声明payable

constructor() payable {}

总结

在第三讲,我们介绍了solidity中的函数类型,比较难理解的是pure和view,在其他语言中没出现过。solidity拥有pure和view两种关键字主要是为了节省gas fee和控制函数权限,这两种方程都是不消耗gas的。下一讲我们会介绍引用和映射两种类型,并介绍更复杂的函数。

Subscribe to 0xAA
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.