Appearance
运算符
几乎每个合约都会对数据进行操作:将一些值转换为另一些值。虽然作用范围可能不同,但运算符是这些修改的核心。
本页面列出了 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 并将结果赋回