Skip to content

运算符

几乎每个合约都会对数据进行操作:将一些值转换为另一些值。虽然作用范围可能不同,但运算符是这些修改的核心。

本页面列出了 Tact 中所有的运算符,按优先级从高到低排序,并附有使用示例。

请注意,Tact 中没有隐式类型转换,所以运算符不能用来添加不同类型的值或在等式中比较它们,除非显式地转换为相同的类型。这可以通过标准库中的某些函数来完成。例如,参见 Int.toString()

优先级

本页面上的所有运算符都按优先级从高到低的顺序给出。优先级用于选择在特定情况下会考虑哪个运算符。每当出现任何歧义时,Tact 会优先考虑优先级较高的运算符。

例如,减号(-)可以被视为减法运算符或否定运算符,后者将表达式的符号从正转为负,反之亦然。在两者之间的歧义情况下,Tact 首先将 - 视为否定运算符。如果对给定的表达式没有意义,那么它才会考虑它作为减法运算符。

考虑以下代码:

solidity
5 + -5; // 这里,减号被视为否定运算符
5 -5;   // 而这里,它被视为减法运算符,尽管格式相同

虽然这个例子可能很简单,但忽视优先级规则经常会导致使用运算符时出现混淆的情况。可以通过在每个操作中使用括号来确保操作的正确顺序,因为括号在所有表达式和运算符中具有最高的优先级。

括号,()

括号(也可以称为圆括号,())更像是标点符号而不是实际的运算符,但它们的优先级高于任何其他运算符。使用括号来覆盖操作顺序:

solidity
5 * 5 - 2;   // 23
5 * (5 - 2); // 15

一元运算符

一元运算符意味着它们只应用于给定表达式的一个操作数。除非空值断言外,所有一元运算符的优先级相同。

一元运算符可以是以下两种类型之一:

  • 前缀 —— 放在表达式之前。
  • 后缀(或后缀)—— 放在表达式之后。

非空断言,!!

一元双感叹号(非空断言)运算符 !! 是一个后缀运算符,它强制非空值并允许直接访问可选变量的值(如果它不是 null)。否则会引发编译错误。可以应用于任何可选变量,无论其非空类型如何。

有关可选变量和字段的更多信息,请参阅Optionals

加号,+

虽然一元加号运算符 + 在 Tact 编译器的语法中被指定,但它只存在作为二元运算符。

否定,-

一元减号(否定)运算符 - 是一个前缀运算符,它将表达式的符号反转。只能应用于 Int 类型的值:

solidity
let five: Int = 5;
five + -five; // 这里,减号是否定运算符,不是减法运算符
-(-1);        // 双重应用返回原始值,即 1
--1;          // 语法错误!

反转,!

一元感叹号(反转)运算符 ! 是一个前缀运算符,它反转表达式的布尔值——将 true 变为 false,反之亦然。只能应用于 Bool 类型的值:

solidity
let iLikeTact: Bool = true;
!iLikeTact; // false
!false;     // true
!!true;     // 语法错误!
!(!true);   // true

二元运算符

二元运算符分为几个小节,按优先级从高到低排序。每个小节内的运算符的优先级与小节本身相同。

乘法、触发、取余

乘法,除法或获得余数。

乘法,*

二元星号(乘法)运算符 * 用于两个值的乘法。可能导致整数溢出。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two * two;         // 4
0 * 1_000_000_000; // 0
-1 * 5;            // -5
 
pow(2, 255) * pow(2, 255); // 构建错误:整数溢出!

除法,/

二元斜杠(除法)运算符 / 用于两个值的整数除法,向零截断。尝试除以零将导致错误,退出代码 4:整数溢出。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two / 2; // 1
two / 1; // 2
-1 / 5;  // -1

取模,%

二元百分号(取模)运算符 % 用于获取整数除法的模数,不应与获取余数混淆。对于同号的两个值,模数和余数操作是等价的,但当操作数符号不同时,模数结果总是与除数(右侧的值)同号,而余数与被除数(左侧的值)同号,这可能使它们相差除数的一个单位。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two % 2; // 0
1 % two; // 1
 
1 % 5;   // 1
-1 % 5;  // 4
1 % -5;  // -4
-1 % -5; // -1

避免两者之间的混淆最简单的方法是优先使用正值,通过 abs(x: Int)

solidity
abs(-1) % abs(-5); // 1

你知道吗,JavaScript 中的 % 作为余数运算符,而不是模数运算符(如在 Tact 中)? 余数(%)- JavaScript模数 - 维基百科

加法、减法

加法或减法。

加法,+

二元加号(加法)运算符 + 用于将数字加在一起。超出 Int 的最大值将导致错误,退出代码 4:整数溢出。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two + 2; // 4
-1 + 1;  // 0
 
pow(2, 254) + pow(2, 254);     // 2 * 2^254
pow(2, 255) + pow(2, 255);     // 构建错误:整数溢出!
pow(2, 255) - 1 + pow(2, 255); // 2^256 - 1,任何整数在 Tact 中的最大值!

减法,-

二元减号(减法)运算符 - 用于从一个数字中减去另一个数字。超出 Int 的最小值将导致错误,退出代码 4:整数溢出。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two - 2; // 0
-1 - 1;  // -2
 
pow(2, 254) - pow(2, 254); // 0
pow(2, 255) - pow(2, 255); // 0
pow(2, 256) - pow(2, 256); // 构建错误:整数溢出!

位运算

操作单个位。

右移,>>

二元大于号(位右移)运算符 >> 返回一个整数,其二进制表示是左操作数值向右移动右操作数指定的位数。超出右侧的位被丢弃,并从左侧补入最左边的位的副本。这种操作也称为“符号传播右移”或“算术右移”,因为结果数字的符号与左操作数的符号相同。这是将左操作数除以 (2^n) 的更有效方法,其中 (n) 等于右操作数。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two >> 1; // 1
4 >> 1;   // 2
5 >> 1;   // 2, 因为整数值向下取整
 
pow(2, 254) >> 254; // 1

位移 - 维基百科位操作 - 维基百科

左移,<<

二元小于号(位左移)运算符 << 返回一个整数,其二进制表示是左操作数值向左移动右操作数指定的位数。超出左侧的位被丢弃,并从右侧补入零位。这是将左操作数乘以 (2^n) 的更有效方法,其中 (n) 等于右操作数。超出 Int 的最大值将导致错误,退出代码 4:整数溢出。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two << 1; // 4
1 << 5;   // 1 * 2^5, 即 32
2 << 5;   // 2 * 2^5, 即 64
 
pow(2, 254) == (1 << 254); // 真
pow(2, 254) == 1 << 254; // 真,无需括号,因为 >> 的优先级高于 ==
pow(2, 255) == 1 << 255; // 真,但我们已经非常接近溢出了!

位移 - 维基百科位操作 - 维基百科

位与,&

二元与号(位与)运算符 & 应用位与操作,对操作数的每对相应位执行逻辑与操作。这在我们想要清除一个数字的选定位时非常有用,其中每个位代表一个单独的标志或布尔状态,这使得可能“存储”多达 257 个布尔值每个整数,因为 Tact 中的所有整数都是 257 位有符号的。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two & 1;          // 0
4 & 1;            // 0
3 & 1;            // 1
1 & 1;            // 1
255 & 0b00001111; // 15

位与 - 维基百科位操作 - 维基百科

位或,|

二元竖杠号(位或)运算符 | 应用位或操作,对操作数的每对相应位执行逻辑或操作。这在我们想要应用特定的位掩码时非常有用。

只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two | 1; // 3
4 | 1;   // 5
3 | 1;   // 3
1 | 1;   // 1
 
255 | 0b00001111;        // 255
0b11110000 | 0b00001111; // 255

位或 - 维基百科位操作 - 维基百科

比较

执行不等和等值检查,找出较大、较小或相等的值。

等于和不等于,== !=

二元等于运算符 == 检查其两个操作数是否相等,返回类型为 Bool 的结果。

二元不等于运算符 != 检查其两个操作数是否不相等,返回类型为 Bool 的结果。

这两个运算符要求操作数类型相同,并且除了 Cell 和 Slice 类型外,不进行隐式类型转换,Cell 和 Slice 类型通过它们的哈希隐式比较。

这些运算符可以应用于以下类型和值列表:

  • Int
  • Bool
  • Address
  • Cell,通过 .hash() 隐式比较
  • Slice,通过 .hash() 隐式比较
  • String
  • map<k, v>,但仅当它们的键和值类型相同时
  • 可选值和 null 值
solidity
// Int:
2 == 3; // false
2 != 3; // true
 
// Bool:
true == true;  // true
false != true; // true
 
// Address:
myAddress() == myAddress(); // true
myAddress() != myAddress(); // false
 
// Cell:
emptyCell() == emptyCell(); // true
emptyCell() != emptyCell(); // false
 
// Slice:
"A".asSlice() == "A".asSlice(); // true
"A".asSlice() != "A".asSlice(); // false
 
// String:
"A" == "A"; // true
"A" != "A"; // false
 
// map<k, v>:
let map1: map<Int, Int> = emptyMap();
let map2: map<Int, Int> = emptyMap();
map1 == map2; // true
map1 != map2; // false
 
// 可选值和 null 值本身
let nullable: Int? = null;
nullable == null; // true
null == null;     // true
nullable != null; // false
null != null;     // false
 
let anotherNullable: Int? = 5;
nullable == anotherNullable; // false
nullable != anotherNullable; // true

大于,>

二元大于运算符 > 如果左操作数大于右操作数,则返回 true,否则返回 false。只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two > 2; // false
-1 > -3; // true

大于或等于,>=

二元大于或等于运算符 >= 如果左操作数大于或等于右操作数,则返回 true,否则返回 false。只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two >= 2; // true
-1 >= -3; // true

小于,<

二元小于运算符 < 如果左操作数小于右操作数,则返回 true,否则返回 false。只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two < 2; // false
-1 < -3; // false

小于或等于,<=

二元小于或等于运算符 <= 如果左操作数小于或等于右操作数,则返回 true,否则返回 false。只能应用于 Int 类型的值:

solidity
let two: Int = 2;
two <= 2; // true
-1 <= -3; // false

逻辑与,&&

二元逻辑与运算符 && 如果两个操作数都为 true,则返回 true,否则返回 false。它是短路的,意味着如果左操作数为 false,它会立即评估整个表达式为 false,而不评估右操作数。

只能应用于 Bool 类型的值:

solidity
let iLikeTact: Bool = true;
iLikeTact && true;  // true
iLikeTact && false; // false, 评估了两个操作数
false && iLikeTact; // false, 没有评估 iLikeTact

逻辑或,||

二元逻辑或运算符 || 只有当两个操作数都为 false 时才返回 false,否则返回 true。它是短路的,意味着如果左操作数为 true,它会立即评估整个表达式为 true,而不评估右操作数。

只能应用于 Bool 类型的值:

solidity
let iLikeSnails: Bool = false;
iLikeSnails && true;  // true
iLikeSnails && false; // false, 评估了两个操作数
true && iLikeSnails;  // true, 没有评估 iLikeSnails

三元运算符,?:

条件(三元)运算符是 Tact 语言中唯一需要三个操作数的运算符:一个条件后跟一个问号(?),然后是一个表达式,如果条件评估为真则执行,后面跟一个冒号(:),最后是一个表达式,如果条件评估为假则执行。这个运算符经常被用作 if...else 语句的替代。

条件必须解析为 Bool 类型:

solidity
// 条件
// ↓
true ? "incredibly so" : "absolutely not"; // "incredibly so"
//     ---------------   ----------------
//     ↑                 ↑
//     |                 替代方案,当条件为假时
//     |
//     后果,当条件为真时
 
2 + 2 == 4 ? true : false; // true

三元运算符是除了赋值相关的运算符外,唯一具有右结合性的运算符。这意味着在模糊情况下,Tact 会优先考虑最长的匹配序列。简而言之,这使得三元运算符的无括号嵌套成为可能,但仅限于替代情况(冒号后的部分):

solidity
// 替代情况不需要额外的括号
false ? 1 : (false ? 2 : 3); // 3
false ? 1 : false ? 2 : 3;   // 也是 3
false ? 1 : true ? 2 : 3;    // 2
 
// 需要额外的括号用于后果情况(? 和 : 之间的部分)
false ? (false ? 1 : 2) : 3; // 3
false ? false ? 1 : 2 : 3;   // 语法错误!
true ? (false ? 1 : 2) : 3;  // 2

赋值,=

赋值运算符 = 用于将值赋给变量,或者 Message 或 Struct 的属性。赋值是一个语句,它不返回值。

solidity
let someVar: Int = 5;    // 这里使用了赋值运算符 `=`...
someVar = 4;             // ...这里也使用了
someVar = (someVar = 5); // 语法错误!

增强赋值

增强(或复合)赋值运算符,如 +=,结合了一个操作和一个赋值。增强赋值是一个语句,它不返回值。

增强赋值在语义上等同于常规赋值,但增加了一个操作:

solidity
let value: Int = 5;
 
// 这个:
value += 5;
// 等价于这个:
value = value + 5;

增强赋值运算符列表:

  • +=,使用加法运算符 +。只能应用于 Int 类型的值。
  • -=,使用减法运算符 -。只能应用于 Int 类型的值。
  • *=,使用乘法运算符 *。只能应用于 Int 类型的值。
  • /=,使用除法运算符 /。只能应用于 Int 类型的值。
  • %=,使用模运算符 %。只能应用于 Int 类型的值。
solidity
let value: Int = 5;
 
// +=
value + 5;         // 加 5
value = value + 5; // 加 5 并将结果赋回
value += value;    // 也加 5 并将结果赋回
 
// -=
value - 5;         // 减 5
value = value - 5; // 减 5 并将结果赋回
value -= value;    // 也减 5 并将结果赋回
 
// *=
value * 5;         // 乘以 5
value = value * 5; // 乘以 5 并将结果赋回
value *= value;    // 也乘以 5 并将结果赋回
 
// /=
value / 5;         // 除以 5
value = value / 5; // 除以 5 并将结果赋回
value /= value;    // 也除以 5 并将结果赋回
 
// %=
value % 5;         // 求模 5
value = value % 5; // 求模 5 并将结果赋回
value %= value;    // 也求模 5 并将结果赋回