---
title: 函数
description: 全局、汇编、本地函数，以及接收器、获取器和存储函数，加上许多允许Tact语言具有极大灵活性和表达能力的属性
---

import { Badge } from '@astrojs/starlight/components';

在 Tact 中，函数可以通过不同的方式定义：

- 全局静态函数
- 扩展函数
- 可变函数
- 原生函数
- 接收器(Receiver)函数
- 获取器(getter)函数

除了 [接收器函数](#receiver-functions)，所有函数的定义（参数列表）和调用（参数列表）都可以使用逗号：

```tact
fun foo(
    a: Int, // trailing comma in parameter lists is allowed
) {}

fun bar() {
    foo(
        5, // trailing comma in argument lists is allowed too!
    );
}
```

## 全局静态函数 {#global-static-functions}

您可以在程序的任何地方定义全局函数：

```tact
fun customPow(a: Int, c: Int): Int {
  let res: Int = 1;
  repeat(c) {
    res *= a;
  }
  return res;
}
```

## 虚拟和抽象函数 {#virtual-and-abstract-functions}

如果 [traits](/zh-cn/book/types#traits) 有 `virtual{:tact}` 关键字，则可以使用 `override{:tact}` 允许继承 [traits](/zh-cn/book/types#traits) 的合约修改内部函数。  函数也可以标记为 `abstract{:tact}`，在这种情况下，继承合约必须定义其实现：

```tact
trait FilterTrait with Ownable {
    // 此 trait 的用户可以重写虚拟函数
    virtual fun filterMessage()：Bool {
        return sender() != self.owner;
    }

    abstract fun specialFilter()：Bool;
}

带有 FilterTrait 的合约 Filter {
    // 覆盖 FilterTrait 的默认行为
    override fun filterMessage()：Bool {
        return true;
    }

    override fun specialFilter()：Bool {
        return true;
    }
}
```

## 扩展函数 {#extension-function}

扩展函数允许你为任何可能的类型实现扩展。

> **警告**
> 第一个参数的名称必须名为 `self`，该参数的类型必须是你要扩展的类型。

```tact
extends fun pow(self: Int, c: Int) {
  let res: Int = 1;
  repeat(c) {
    res = res * self;
  }
  return res;
}
```

## 可变函数

可变函数是对值进行变更，将其替换为执行结果。 可变函数是对数值进行变异，用执行结果代替数值。 要执行突变，函数必须改变 `self` 值。

```tact
extends mutates fun customPow(self: Int, c: Int) {
    let res: Int = 1;
    repeat(c) {
        res *= self;
    }
    self = res;
}
```

## 原生函数

原生函数是 FunC 函数的直接绑定：

> **注**
> 原生函数也可以是可变函数和扩展函数。

```tact
@name(store_uint)
native storeUint(s: Builder, value: Int, bits: Int): Builder;

@name(load_int)
extends mutates native loadInt(self: Slice, l: Int): Int;
```

## 接收器函数 {#receiver-functions}

接收器函数是负责在合约中接收信息的特殊函数，只能在合约或trait中定义。

```tact
contract Treasure {
    // This means that this contract can receive the comment "Increment" and this function would be called for such messages
    receive("Increment") {
        self.counter += 1;
    }
}
```

## 获取器函数 {#getter-functions}

获取器函数定义智能合约上的获取器，只能在合约或特征中定义。
Getter 函数不能用来读取其他合约的状态：如果您需要获取一些数据，您需要通过发送带有请求的消息和定义接收器来读取请求答案。

```tact
contract Treasure {
    get fun counter(): Int {
        return self.counter;
    }
}
```

### 明确解决方法 ID 碰撞的问题

<Badge text="Available since Tact 1.6" variant="tip" size="medium"/><p/>

与 TON 合约中的其他函数一样，getter 函数都有其 _唯一_ 的关联函数选择器，它们是 $19$ 位有符号整数标识符，通常称为 _方法 ID_。

getter 的方法 ID 是使用 [CRC16](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) 算法从其名称派生的，如下所示：`(crc16(<function_name>) & 0xffff) | 0x10000`。此外，Tact 编译器有条件地保留一些方法 ID 用于[支持的接口的 getter](/book/contracts#interfaces)，即：$113617$ 用于 `supported_interfaces`，$115390$ 用于 `lazy_deployment_completed`，以及 $121275$ 用于 `get_abi_ipfs`。

有时，不同名称的 getter 最终会得到相同的方法 ID。如果发生这种情况，您可以重命名某些 getter，或者手动指定方法 ID 作为[编译时](/zh-cn/ref/core-comptime)表达式，如下所示：


```tact
contract ManualMethodId {
    const methodId: Int = 16384 + 42;

    get(self.methodId) fun methodId1(): Int {
        return self.methodId;
    }

    get(crc32("crc32") + 42 & 0x3ffff | 0x4000)
    fun methodId2(): Int {
        return 0;
    }
}
```

请注意，_不能_使用 TVM 保留的方法 ID，也不能使用某些初始正整数，因为编译器会将其用作函数选择器。

用户指定的方法 ID 是 19 位有符号整数，因此可以使用从 $-2^{18}$ 到 $-5$ 以及从 $2^{14}$ 到 $2^{18}$ 的整数。- 1$.

此外，还有一些方法 ID 是为 Tact 编译器在编译过程中插入的获取器保留的，它们是 113617、115390 和 121275。
