Solidity 函数

本章学习 Solidity 函数的定义和使用方法。

视频Bilibili  |  Youtube
官网binschool.app
推特@BinSchool    DiscordBinDAO   微信:bkra50 

Solidity 中,函数是合约中的可执行代码块,用于定义合约的行为和操作。

函数里包含一系列语句和业务逻辑,可以接受输入的参数,执行特定的操作,并返回结果。

函数和状态变量是 Solidity 智能合约中最重要的组成部分。

1. 函数定义

Solidity 中, 定义函数的语法如下:

function function_name(<parameter list>) <visibility> <state mutability> [returns(<return type>)] {
    ......
}

关键字 function 用来定义一个函数,后面跟着函数名、参数列表、可见性(visibility)、状态可变性(state mutability)以及返回值。

例如,定义一个加法函数:

function add(uint a, uint b) public pure returns(uint) {
    return a + b;
}

这个函数的名称是 add,它需要两个整型参数 a 和 b,函数的可见性为 public,状态可变性是 pure,最后返回一个整型值。

2. 命名规则

函数的命名规则与变量相同。函数名可以是由字母、数字和下划线“_”组成的字符串,但不能包含空格或其他特殊字符,也不能以数字开头。

通常,比较规范的写法是采用驼峰形式,以小写字母或者下划线“_”开头。

例如:increaseAccount、_burnToken、splitString1 等,这都是正确的函数名字,而 1trim、#burnToken 就不正确,它们是无法通过编译的。

3. 可见性

函数可见性共有 4 种,分别是 privatepublicinternalexternal,用于限制函数的使用范围。

函数可见性的规则与状态变量基本相同,但比状态变量多了一种 external

private

private 修饰的函数只能在所属的智能合约的内部调用。

public

public 修饰的函数可以从任何地方调用。它既可以在智能合约的内部调用,也可以从智能合约的外部调用。

internal

internal 修饰的函数可以在所属的智能合约的内部调用,也可以在继承合约中调用。

external

external 修饰的函数只能从智能合约的外部调用,不能在合约的内部直接调用。如果一定要在合约内部使用的话,那么需要在函数前面加上“this.”。

所以,如果智能合约的某个函数要提供给外部使用,那么它的可见性必须为 external 或者 public

示例

Solidity 的新版本中,函数定义中必须显式地指定可见性,不能省略,否则无法通过编译。

我们要根据实际的业务需求,来确定函数的可见性。不恰当的可见性会造成严重的安全问题,这类事故在实际中经常发生。

如果你有兴趣,可以看我的《智能合约安全系列》视频中的访问控制漏洞攻击。

我们来看一个完整的使用函数可见性的例子:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FunctionVisibility {
    // 可见性为 private,只能在合约内部调用
    function add(uint a, uint b) private pure returns(uint){
      return a + b;
    }
    // 可见性为 public,合约内部和外部均可调用
    function sub(uint a, uint b) public pure returns(uint){
      return a - b;
    }
    // 可见性为 internal,合约内部和继承合约中可以调用
    function mul(uint a, uint b) internal pure returns(uint){
      return a * b;
    }
    // 可见性为 external,只能在合约外部调用
    function div(uint a, uint b) external pure returns(uint){
      return a / b;
    }
}

我们将合约代码复制到 Remix,进行编译,并部署到区块链上:

从部署后的结果来看,可见性为 public 的函数 sub,可见性为 external 的 div,它们对于外部来说是可以访问的。

而可见性为 private 的 add,可见性为 internal 的 mul,它们对于外部来说都是不可见的,当然也就无法从外部访问了。

4. 返回值

Solidity 中,函数可以没有返回值,也可以返回一个或多个值。返回值可以是任何有效的数据类型。

在函数声明中,需要使用 returns 关键字指定返回值的类型。

返回形式

在函数体中,可以有两种方式来返回结果值:

  1. 使用 return 关键字指定返回值;
  2. 使用返回值的参数名称指定返回值。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FunctionReturn {
  // 使用 `return` 关键字指定返回值
  function add1(uint a, uint b) public pure returns (uint){
    return a + b;
  }
  // 使用返回值的参数名称指定返回值
  function add2(uint a, uint b) public pure returns (uint result){
    result = a + b;
  }
}

多返回值

Solidity 中,函数可以有多个返回值,使用 return 返回结果时,需要使用括号包裹所有的返回值。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FunctionMultiReturn {
  // 多返回值
  function addAndMul(uint a, uint b) public pure returns (uint, uint){
    uint sum = a + b;
    uint product = a * b;
    return (sum, product);
  }
}

5. 函数调用

要调用函数,只需使用函数名,并传入相应的参数即可。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FunctionCall {
  function add(uint a, uint b) internal pure returns (uint){
    return a + b;
  }

  function mul(uint a, uint b) internal pure returns (uint){
    return a * b;
  }

  function addAndMul(uint a, uint b) public pure returns (uint, uint){
    uint sum = add(a, b); // 调用函数 add
    uint product = mul(a, b); // 调用函数 mul
    return (sum, product);
  }
}

函数是智能合约的核心组成部分,用于实现合约的业务逻辑和功能。

通过定义和调用函数,可以在合约中实现各种业务逻辑和操作,与其它合约进行交互,并实现合约与外部世界的通信。

6. 函数重载

Solidity 语言支持函数重载,是指在同一合约中允许定义多个同名函数,但必须具有不同的参数。

所谓不同的参数,是指参数个数、参数类型或者参数顺序,必须其中之一不同。我们可以参照下面的合约例子进行理解。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Overload {
  // 一个参数
  function foo(uint256 a) pure public returns(uint256){
    return a;
  }
  // 两个参数,参数数量不同
  function foo(uint256 a, address b) pure public returns(uint256, address){
    return (a,b);
  }
  // 两个参数,参数类型不同
  function foo(uint256 a, uint256 b) pure public returns(uint256, uint256){
    return (a,b);
  }
  // 两个参数,参数顺序不同
  function foo(address b, uint256 a) pure public returns(uint256, address){
    return (a,b);
  }
}