跳转到内容

与 func 的兼容性

Tact 本身编译为 FunC,并将其所有实体直接映射到各种 FunC 和 TL-B 类型。

转换类型

Tact 中的 原始类型可直接映射到 FunC 中的类型。

所有关于复制变量的规则都是一样的。其中一个最大的不同是,在 Tact 中没有可见的突变操作符,大多数 切片操作都是就地突变变量。

转换序列化

在 Tact 中,StructsMessages的序列化是自动进行的,不像 FunC 需要手动定义序列化逻辑。

Tact 的自动布局算法是贪婪的。这意味着它会获取下一个变量,计算其大小,并尝试将其放入当前单元格。如果不合适,它就会创建一个新单元格并继续。所有用于自动布局的内部结构都会在分配前被扁平化。

除了 Address 以外,所有可选类型在 TL-B 中都序列化为 Maybe

没有对 Either 的支持,因为它没有定义在某些情况下序列化时应选择什么。

实例

// _ value1:int257 = SomeValue;
struct SomeValue {
value1: Int; // 默认为 257 位
}
// _ value1:int256 value2:uint32 = SomeValue;
struct SomeValue {
value1: Int as int256;
value2int as uint32;
}
// _ value1:bool value2:Maybe bool = SomeValue;
struct SomeValue {
value1: Bool;
value2Bool?;
}
// _ cell:^cell = SomeValue;
struct SomeValue {
cellCell; // 总是作为引用存储
}
// _ cell:^slice = SomeValue;
struct SomeValue {
cellSlice; // 总是作为引用存储
}
// _ value1:int256 value2:int256 value3:int256 ^[value4:int256] = SomeValue;
struct SomeValue {
value1: Int as int256;
value2: Int as int256;
value3: Int as int256;
value4: Int as int256;
}
// _ value1:int256 value2:int256 value3:int256 ^[value4:int256] flag:bool = SomeValue;
struct SomeValue {
value1: Int as int256;
value2: Int as int256;
value3: Int as int256;
flagBool; // Flag 写在 value4 之前,以避免自动布局将其分配到下一个单元格
value4: Int as int256;
}
// _ value1:int256 value2:int256 value3:int256 ^[value4:int256 flag:bool] = SomeValue;
struct SomeValue {
value1: Int as int256;
value2: Int as int256;
value3: Int as int256;
value4: Int as int256;
flag: Bool;
}
// _ value1:int256 value2:^TailString value3:int256 = SomeValue;
struct SomeValue {
value1: Int as int256;
value2String;
value3: Int as int256;
}

将收到的信息转换为 op 操作

Tact 会为每条接收到的键入信息生成一个唯一的 op,但它可以被覆盖。

下面是 FunC.NET 中的代码

() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
;; 传入消息代码...
;; 接收 MessageWithGeneratedOp 消息
if (op == 1180414602) {
;; 代码...
}
;; 接收 MessageWithOverwrittenOP 消息
if (op == 291) {
;; 代码...
}
}

在 Tact 中变成了这样:

message MessageWithGeneratedOp {
amount: Int as uint32;
}
message(0x123) MessageWithOverwrittenOP {
amount: Int as uint32;
}
contract Contract {
// Contract Body...
receive(msg: MessageWithGeneratedOp) {
// code...
}
receive(msg: MessageWithOverwrittenOP) {
// code...
}
}

Convert get-methods

你可以用与 FunC 的 get 方法兼容的 Tact 来表达除 list-style-lists 以外的所有内容。

基本返回类型

如果一个 get 方法在 FunC 中返回一个基元,那么在 Tact 中也可以用同样的方法实现它。

下面是 FunC 中的代码

int seqno() method_id {
return 0;
}

在 Tact 中变成了这样:

get fun seqno(): Int {
return 0;
}

张量返回类型

在 FunC 中,张量类型 (int, int)(int, (int)) 是有区别的,但对于 TVM 来说没有区别,它们都表示两个整数的堆栈。

要转换从 FunC get 方法返回的张量,需要定义一个 Struct,它与张量的字段类型相同,顺序也相同。

下面是 FunC 中的代码

(int, slice, slice, cell) get_wallet_data() method_id {
return ...;
}

在 Tact 中变成了这样:

struct JettonWalletData {
balance: Int;
owner: Address;
master: Address;
walletCode: Cell;
}
contract JettonWallet {
get fun get_wallet_data(): JettonWalletData {
return ...;
}
}

元组返回类型

在 FunC 中,如果返回的是元组而不是张量,则需要遵循张量类型的流程,但要将 “get ”方法的返回类型定义为可选类型。

下面是 FunC 中的代码

[int, int] get_contract_state() method_id {
return ...;
}

在 Tact 中变成了这样:

struct ContractState {
valueA: Int;
valueB: Int;
}
contract StatefulContract {
get fun get_contract_state(): ContractState? {
return ...;
}
}

混合元组和张量返回类型

如果某些张量是元组,则需要像前面的步骤一样定义结构体,而元组必须定义为单独的 [Struct](/book/structs-and-messages#structs)。

下面是 FunC 中的代码

(int, [int, int]) get_contract_state() method_id {
return ...;
}

在 Tact 中变成了这样:

struct ContractStateInner {
valueA: Int;
valueB: Int;
}
struct ContractState {
valueA: Int;
valueB: ContractStateInner;
}
contract StatefulContract {
get fun get_contract_state(): ContractState {
return ...;
}
}

参数 制图

获取 “方法参数的转换非常简单。每个参数都_原样_映射到 FunC one,每个元组都映射到 结构

下面是 FunC 中的代码

(int, [int, int]) get_contract_state(int arg1, [int,int] arg2) method_id {
return ...;
}

在 Tact 中变成了这样:

struct ContractStateArg2 {
valueA: Int;
valueB: Int;
}
struct ContractStateInner {
valueA: Int;
valueB: Int;
}
struct ContractState {
valueA: Int;
valueB: ContractStateInner;
}
contract StatefulContract {
get fun get_contract_state(arg1: Int, arg2: ContractStateArg2): ContractState {
return ContractState{
valueA: arg1,
valueB: ContractStateInner{
valueA: arg2.valueA,
valueB: arg2.valueB, // trailing comma is allowed
}, // trailing comma is allowed
};
}
}