Cover photo

函数

函数

Solidity语言的函数非常灵活,可以进行各种复杂的操作。本教程中,我们将会概述函数的基本概念,并通过一些示例演示如何使用函数。

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

看着有点复杂,让我们从前往后逐个解释(方括号的是可写可不写的关键字):

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

  2. <function name>:函数名。

  3. (<parameter types>):圆括号内写入函数参数,即输入到函数的变量类型和名称。

  4. {internal|external|public|private}:函数可见性说明符,共有四种。

    • public:内部和外部均可见。

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

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

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

    注意1:合约中定义的函数需要明确指定可见性,它们没有默认值。

    注意2public|private|internal也可用于修饰状态变量。public变量会自动生成同名的getter函数,用于查询数值。未标明可见性的状态变量默认为internal

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

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

到底什么是PureView

刚开始学习solidity时,pureview关键字可能令人费解,因为其他编程语言中没有类似的关键字。solidity引入这两个关键字主要因为以太坊交易需要支付gas fee。合约的状态变量存储在链上,gas fee很贵,如果计算不改变链上状态,就可以不用支付gas。博客pureview关键字的函数是不改写链上状态的,因此用户直接调用它们是不需要支付gas(注意,合约中非pure/view函数调用pure/view函数时需要支付gas)

在以太坊中,以下语句被视为修改链上状态:

  1. 写入状态变量。

  2. 释放事件。

  3. 创建其他合约。

  4. 使用selfdestruct

  5. 通过调用发送以太币。

  6. 调用任何未标记viewpure的函数。

  7. 使用低级别调用(low-level calls)。

  8. 使用包含某些操作码内联汇编。

  • pure:函数不读取和修改链上状态变量。

  • view:函数读取但不写入链上状态变量。

  • pureview:可读可写链上状态变量。

代码

1. pure 和 view

我们在合约定义一个状态变量number,初始化为5。

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

定义一个add()函数,每次调用会让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,返回一个新的变量。

返回值:return和returns

solidity中与函数输出香港的有两个关键字:returnreturns。他们的区别在于:

  • returns:跟函数后面,用于声明返回变量类型和变量名。

  • return:用于函数主体中,返回指定的变量。

    // 返回多个变量
    function returnMultiple() public pure returns(uint256, bool, uint256[3] memory){
        return(1, true, [uint256(1),2,5]);
    }

在上述代码中,我们利用returns关键字声明了有多个返回值的returnMultiple()函数,然后我们在函数主体使用return(1, true, [uint256(1),2,5])确定了返回值。

命名式返回

我们可以在returns中标明返回变量的名称。Solidity会初始化这些变量,并且自动返回函数值,无需使用return

    // 命名式返回
    function returnNamed() public pure returns(uint256 _number, bool _bool, uint256[3] memory _array){
        _number = 2;
        _bool = false;
        _array = [uint256(3),2,1];
    }

在上述代码中,我们用returns(uint256 _number, bool _bool, uint256[3] memory _array)声明了返回变量类型及变量名。这样,在主体中只需为变量_number_bool、和_array赋值,即可自动返回。

当然,你也可以在命名式返回中用return来返回变量:

    // 命名式返回,依然支持return
    function returnNamed2() public pure returns(uint256 _number, bool _bool, uint256[3] memory _array){
        return(1, true, [uint256(1),2,5]);
    }

解析式赋值

Solidity支持使用解析式赋值柜子来读取函数的全部或部分返回值。

  • 读取所有返回值:声明变量,然后将要赋值的变量用,隔开,按顺序排列。

  • 读取部分返回值:声明要读取的返回值对应的变量,不读取的留空。在下面的代码中,我们只读取了_bool,而不读取返回的_number_array

// 读取返回值,解构式赋值
    function readReturn() public pure{
        // 读取全部返回值
        uint256 _number;
        bool _bool;
        bool _bool2;
        uint256[3] memory _array;
        (_number, _bool, _array) = returnNamed();
        
        // 读取部分返回值,解构式赋值
        (, _bool2, ) = returnNamed();
    }