Token
Token 是由正则(非递归)语言定义的语法中的基本产生式。Rust 源代码输入可以分解为以下几种 token
在本文档的语法中,“简单” token 以 字符串表产生式 形式给出,并以 monospace
字体显示。
字面量
字面量是在 字面量表达式 中使用的 token。
示例
字符和字符串
示例 | # 集合1 | 字符 | 转义符 | |
---|---|---|---|---|
字符 | 'H' | 0 | 所有 Unicode | 引号 & ASCII & Unicode |
字符串 | "hello" | 0 | 所有 Unicode | 引号 & ASCII & Unicode |
原始字符串 | r#"hello"# | <256 | 所有 Unicode | N/A |
字节 | b'H' | 0 | 所有 ASCII | 引号 & 字节 |
字节字符串 | b"hello" | 0 | 所有 ASCII | 引号 & 字节 |
原始字节字符串 | br#"hello"# | <256 | 所有 ASCII | N/A |
C 字符串 | c"hello" | 0 | 所有 Unicode | 引号 & 字节 & Unicode |
原始 C 字符串 | cr#"hello"# | <256 | 所有 Unicode | N/A |
每个字面量两侧 #
的数量必须相等。
注意:字符和字符串字面量 token 永远不包含紧跟
U+000A
(LF) 的U+000D
(CR) 序列:这对序列之前会被转换为单个U+000A
(LF)。
ASCII 转义符
名称 | |
---|---|
\x41 | 7 位字符代码(正好 2 位数字,最大到 0x7F) |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 |
\\ | 反斜杠 |
\0 | 空字符 |
字节转义符
名称 | |
---|---|
\x7F | 8 位字符代码(正好 2 位数字) |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 |
\\ | 反斜杠 |
\0 | 空字符 |
Unicode 转义符
名称 | |
---|---|
\u{7FFF} | 24 位 Unicode 字符代码(最多 6 位数字) |
引号转义符
名称 | |
---|---|
\' | 单引号 |
\" | 双引号 |
数字
所有数字字面量都允许使用 _
作为视觉分隔符:1_234.0E+18f64
后缀
后缀是字面量主要部分之后(没有中间的空白符)的字符序列,与非原始标识符或关键字的形式相同。
词法分析器
SUFFIX : IDENTIFIER_OR_KEYWORD
SUFFIX_NO_E : SUFFIX 不以e
或E
开头
任何带有任何后缀的字面量(字符串、整数等)作为 token 都是有效的。
带有任何后缀的字面量 token 都可以传递给宏而不会产生错误。宏本身将决定如何解释这样的 token 以及是否产生错误。特别是,用于按示例宏的 literal
片段说明符匹配带有任意后缀的字面量 token。
#![allow(unused)] fn main() { macro_rules! blackhole { ($tt:tt) => () } macro_rules! blackhole_lit { ($l:literal) => () } blackhole!("string"suffix); // OK blackhole_lit!(1suffix); // OK }
但是,解释为字面量表达式或模式的字面量 token 上的后缀是受限制的。非数字字面量 token 上的任何后缀都会被拒绝,而数字字面量 token 仅接受来自以下列表的后缀。
整数 | 浮点数 |
---|---|
u8 , i8 , u16 , i16 , u32 , i32 , u64 , i64 , u128 , i128 , usize , isize | f32 , f64 |
字符和字符串字面量
字符字面量
词法分析器
CHAR_LITERAL
'
( ~['
\
\n \r \t] | QUOTE_ESCAPE | ASCII_ESCAPE | UNICODE_ESCAPE )'
SUFFIX?QUOTE_ESCAPE
\'
|\"
ASCII_ESCAPE
\x
OCT_DIGIT HEX_DIGIT
|\n
|\r
|\t
|\\
|\0
UNICODE_ESCAPE
\u{
( HEX_DIGIT_
* )1..6}
字符字面量 是用两个 U+0027
(单引号)字符括起来的单个 Unicode 字符,但 U+0027
本身除外,它必须由前面的 U+005C
字符 (\
) 转义。
字符串字面量
词法分析器
STRING_LITERAL
"
(
~["
\
IsolatedCR]
| QUOTE_ESCAPE
| ASCII_ESCAPE
| UNICODE_ESCAPE
| STRING_CONTINUE
)*"
SUFFIX?STRING_CONTINUE
\
后跟 \n
字符串字面量 是用两个 U+0022
(双引号)字符括起来的任何 Unicode 字符序列,但 U+0022
本身除外,它必须由前面的 U+005C
字符 (\
) 转义。
字符串字面量中允许换行符,由字符 U+000A
(LF) 表示。当未转义的 U+005C
字符 (\
) 紧接在换行符之前出现时,换行符不会出现在 token 表示的字符串中。有关详细信息,请参阅 字符串延续转义符。字符 U+000D
(CR) 不得出现在字符串字面量中,除非作为此类字符串延续转义符的一部分。
字符转义符
一些额外的转义符在字符或非原始字符串字面量中可用。转义符以 U+005C
(\
) 开头,并继续使用以下形式之一
- 7 位代码点转义符 以
U+0078
(x
) 开头,后跟正好两位十六进制数字,值最大为0x7F
。它表示 ASCII 字符,其值等于提供的十六进制值。不允许更高的值,因为它们是指 Unicode 代码点还是字节值是模棱两可的。
- 24 位代码点转义符 以
U+0075
(u
) 开头,后跟最多六位十六进制数字,用花括号U+007B
({
) 和U+007D
(}
) 括起来。它表示 Unicode 代码点,其值等于提供的十六进制值。
- 空白字符转义符 是字符
U+006E
(n
)、U+0072
(r
) 或U+0074
(t
) 之一,分别表示 Unicode 值U+000A
(LF)、U+000D
(CR) 或U+0009
(HT)。
- 空字符转义符 是字符
U+0030
(0
),表示 Unicode 值U+0000
(NUL)。
- 反斜杠转义符 是字符
U+005C
(\
),必须对其进行转义才能表示自身。
原始字符串字面量
词法分析器
RAW_STRING_LITERAL
r
RAW_STRING_CONTENT SUFFIX?RAW_STRING_CONTENT
"
( ~ IsolatedCR )* (非贪婪)"
|#
RAW_STRING_CONTENT#
原始字符串字面量不处理任何转义符。它们以字符 U+0072
(r
) 开头,后跟少于 256 个字符 U+0023
(#
) 和一个 U+0022
(双引号)字符。
原始字符串主体 可以包含除 U+000D
(CR) 之外的任何 Unicode 字符序列。它仅由另一个 U+0022
(双引号)字符终止,后跟与开头的 U+0022
(双引号)字符之前的 U+0023
(#
) 字符数量相同的字符。
原始字符串主体中包含的所有 Unicode 字符都表示它们自身,字符 U+0022
(双引号)(除非后跟至少与用于启动原始字符串字面量的 U+0023
(#
) 字符一样多的字符)或 U+005C
(\
) 没有任何特殊含义。
字符串字面量示例
#![allow(unused)] fn main() { "foo"; r"foo"; // foo "\"foo\""; r#""foo""#; // "foo" "foo #\"# bar"; r##"foo #"# bar"##; // foo #"# bar "\x52"; "R"; r"R"; // R "\\x52"; r"\x52"; // \x52 }
字节和字节字符串字面量
字节字面量
词法分析器
BYTE_LITERAL
b'
( ASCII_FOR_CHAR | BYTE_ESCAPE )'
SUFFIX?ASCII_FOR_CHAR
任何 ASCII (即 0x00 到 0x7F),除了'
,\
, \n, \r 或 \tBYTE_ESCAPE
\x
HEX_DIGIT HEX_DIGIT
|\n
|\r
|\t
|\\
|\0
|\'
|\"
字节字面量 是单个 ASCII 字符(在 U+0000
到 U+007F
范围内)或单个 转义符,前面是字符 U+0062
(b
) 和 U+0027
(单引号),后面是字符 U+0027
。如果字符 U+0027
出现在字面量中,则必须由前面的 U+005C
(\
) 字符 转义。它等效于 u8
无符号 8 位整数数字字面量。
字节字符串字面量
词法分析器
BYTE_STRING_LITERAL
b"
( ASCII_FOR_STRING | BYTE_ESCAPE | STRING_CONTINUE )*"
SUFFIX?ASCII_FOR_STRING
任何 ASCII (即 0x00 到 0x7F),除了"
,\
和 IsolatedCR
非原始字节字符串字面量 是 ASCII 字符和转义符的序列,前面是字符 U+0062
(b
) 和 U+0022
(双引号),后面是字符 U+0022
。如果字符 U+0022
出现在字面量中,则必须由前面的 U+005C
(\
) 字符 转义。或者,字节字符串字面量可以是原始字节字符串字面量,定义如下。
字节字符串字面量中允许换行符,由字符 U+000A
(LF) 表示。当未转义的 U+005C
字符 (\
) 紧接在换行符之前出现时,换行符不会出现在 token 表示的字符串中。有关详细信息,请参阅 字符串延续转义符。字符 U+000D
(CR) 不得出现在字节字符串字面量中,除非作为此类字符串延续转义符的一部分。
一些额外的转义符在字节或非原始字节字符串字面量中可用。转义符以 U+005C
(\
) 开头,并继续使用以下形式之一
- 字节转义符 以
U+0078
(x
) 开头,后跟正好两位十六进制数字。它表示字节,其值等于提供的十六进制值。
- 空白字符转义符 是字符
U+006E
(n
)、U+0072
(r
) 或U+0074
(t
) 之一,分别表示字节值0x0A
(ASCII LF)、0x0D
(ASCII CR) 或0x09
(ASCII HT)。
- 空字符转义符 是字符
U+0030
(0
),表示字节值0x00
(ASCII NUL)。
- 反斜杠转义符 是字符
U+005C
(\
),必须对其进行转义才能表示其 ASCII 编码0x5C
。
原始字节字符串字面量
词法分析器
RAW_BYTE_STRING_LITERAL
br
RAW_BYTE_STRING_CONTENT SUFFIX?RAW_BYTE_STRING_CONTENT
"
ASCII_FOR_RAW* (非贪婪)"
|#
RAW_BYTE_STRING_CONTENT#
ASCII_FOR_RAW
任何 ASCII (即 0x00 到 0x7F),除了 IsolatedCR
原始字节字符串字面量不处理任何转义符。它们以字符 U+0062
(b
) 开头,后跟 U+0072
(r
),后跟少于 256 个字符 U+0023
(#
),以及一个 U+0022
(双引号)字符。
原始字符串主体 可以包含除 U+000D
(CR) 之外的任何 ASCII 字符序列。它仅由另一个 U+0022
(双引号)字符终止,后跟与开头的 U+0022
(双引号)字符之前的 U+0023
(#
) 字符数量相同的字符。原始字节字符串字面量不能包含任何非 ASCII 字节。
原始字符串主体中包含的所有字符都表示它们的 ASCII 编码,字符 U+0022
(双引号)(除非后跟至少与用于启动原始字节字符串字面量的 U+0023
(#
) 字符一样多的字符)或 U+005C
(\
) 没有任何特殊含义。
字节字符串字面量示例
#![allow(unused)] fn main() { b"foo"; br"foo"; // foo b"\"foo\""; br#""foo""#; // "foo" b"foo #\"# bar"; br##"foo #"# bar"##; // foo #"# bar b"\x52"; b"R"; br"R"; // R b"\\x52"; br"\x52"; // \x52 }
C 字符串和原始 C 字符串字面量
C 字符串字面量
词法分析器
C_STRING_LITERAL
c"
(
~["
\
IsolatedCR NUL]
| BYTE_ESCAPE 除了\0
或\x00
| UNICODE_ESCAPE 除了\u{0}
,\u{00}
, …,\u{000000}
| STRING_CONTINUE
)*"
SUFFIX?
C 字符串字面量 是 Unicode 字符和转义符的序列,前面是字符 U+0063
(c
) 和 U+0022
(双引号),后面是字符 U+0022
。如果字符 U+0022
出现在字面量中,则必须由前面的 U+005C
(\
) 字符 转义。或者,C 字符串字面量可以是原始 C 字符串字面量,定义如下。
C 字符串被字节 0x00
隐式终止,因此 C 字符串字面量 c""
等效于手动从字节字符串字面量 b"\x00"
构造 &CStr
。除了隐式终止符外,字节 0x00
在 C 字符串中是不允许的。
C 字符串字面量中允许换行符,由字符 U+000A
(LF) 表示。当未转义的 U+005C
字符 (\
) 紧接在换行符之前出现时,换行符不会出现在 token 表示的字符串中。有关详细信息,请参阅 字符串延续转义符。字符 U+000D
(CR) 不得出现在 C 字符串字面量中,除非作为此类字符串延续转义符的一部分。
一些额外的转义符在非原始 C 字符串字面量中可用。转义符以 U+005C
(\
) 开头,并继续使用以下形式之一
- 字节转义符 以
U+0078
(x
) 开头,后跟正好两位十六进制数字。它表示字节,其值等于提供的十六进制值。
- 24 位代码点转义符 以
U+0075
(u
) 开头,后跟最多六位十六进制数字,用花括号U+007B
({
) 和U+007D
(}
) 括起来。它表示 Unicode 代码点,其值等于提供的十六进制值,编码为 UTF-8。
- 空白字符转义符 是字符
U+006E
(n
)、U+0072
(r
) 或U+0074
(t
) 之一,分别表示字节值0x0A
(ASCII LF)、0x0D
(ASCII CR) 或0x09
(ASCII HT)。
- 反斜杠转义符 是字符
U+005C
(\
),必须对其进行转义才能表示其 ASCII 编码0x5C
。
C 字符串表示没有定义编码的字节,但 C 字符串字面量可能包含高于 U+007F
的 Unicode 字符。此类字符将被替换为该字符的 UTF-8 表示形式的字节。
以下 C 字符串字面量是等效的
#![allow(unused)] fn main() { c"æ"; // LATIN SMALL LETTER AE (U+00E6) c"\u{00E6}"; c"\xC3\xA6"; }
版本差异:C 字符串字面量在 2021 版本或更高版本中被接受。在早期版本中,token
c""
被词法分析为c ""
。
原始 C 字符串字面量
词法分析器
RAW_C_STRING_LITERAL
cr
RAW_C_STRING_CONTENT SUFFIX?RAW_C_STRING_CONTENT
"
( ~ IsolatedCR NUL )* (非贪婪)"
|#
RAW_C_STRING_CONTENT#
原始 C 字符串字面量不处理任何转义符。它们以字符 U+0063
(c
) 开头,后跟 U+0072
(r
),后跟少于 256 个字符 U+0023
(#
),以及一个 U+0022
(双引号)字符。
原始 C 字符串主体 可以包含除 U+0000
(NUL) 和 U+000D
(CR) 之外的任何 Unicode 字符序列。它仅由另一个 U+0022
(双引号)字符终止,后跟与开头的 U+0022
(双引号)字符之前的 U+0023
(#
) 字符数量相同的字符。
原始 C 字符串主体中包含的所有字符都以 UTF-8 编码形式表示自身。字符 U+0022
(双引号)(除非后跟至少与用于启动原始 C 字符串字面量的 U+0023
(#
) 字符一样多的字符)或 U+005C
(\
) 没有任何特殊含义。
版本差异:原始 C 字符串字面量在 2021 版本或更高版本中被接受。在早期版本中,token
cr""
被词法分析为cr ""
,而cr#""#
被词法分析为cr #""#
(这是不符合语法的)。
C 字符串和原始 C 字符串字面量示例
#![allow(unused)] fn main() { c"foo"; cr"foo"; // foo c"\"foo\""; cr#""foo""#; // "foo" c"foo #\"# bar"; cr##"foo #"# bar"##; // foo #"# bar c"\x52"; c"R"; cr"R"; // R c"\\x52"; cr"\x52"; // \x52 }
数字字面量
数字字面量 是整数文字或浮点数文字。识别这两种字面量的语法是混合的。
整数文字
词法分析器
INTEGER_LITERAL
( DEC_LITERAL | BIN_LITERAL | OCT_LITERAL | HEX_LITERAL ) SUFFIX_NO_E?DEC_LITERAL
DEC_DIGIT (DEC_DIGIT|_
)*BIN_LITERAL
0b
(BIN_DIGIT|_
)* BIN_DIGIT (BIN_DIGIT|_
)*OCT_LITERAL
0o
(OCT_DIGIT|_
)* OCT_DIGIT (OCT_DIGIT|_
)*HEX_LITERAL
0x
(HEX_DIGIT|_
)* HEX_DIGIT (HEX_DIGIT|_
)*BIN_DIGIT : [
0
-1
]OCT_DIGIT : [
0
-7
]DEC_DIGIT : [
0
-9
]HEX_DIGIT : [
0
-9
a
-f
A
-F
]
整数文字 有四种形式之一
- 十进制文字 以十进制数字开头,并继续包含任何十进制数字和下划线的混合。
- 十六进制文字 以字符序列
U+0030
U+0078
(0x
) 开头,并继续作为十六进制数字和下划线的任何混合(至少一个数字)。
- 八进制文字 以字符序列
U+0030
U+006F
(0o
) 开头,并继续作为八进制数字和下划线的任何混合(至少一个数字)。
- 二进制文字 以字符序列
U+0030
U+0062
(0b
) 开头,并继续作为二进制数字和下划线的任何混合(至少一个数字)。
与任何字面量一样,整数文字后面可以跟一个后缀(紧接着,没有任何空格),如上所述。后缀不能以 e
或 E
开头,因为这将被解释为浮点数文字的指数。有关这些后缀的效果,请参阅 整数文字表达式。
作为文字表达式被接受的整数文字示例
#![allow(unused)] fn main() { #![allow(overflowing_literals)] 123; 123i32; 123u32; 123_u32; 0xff; 0xff_u8; 0x01_f32; // integer 7986, not floating-point 1.0 0x01_e3; // integer 483, not floating-point 1000.0 0o70; 0o70_i16; 0b1111_1111_1001_0000; 0b1111_1111_1001_0000i64; 0b________1; 0usize; // These are too big for their type, but are accepted as literal expressions. 128_i8; 256_u8; // This is an integer literal, accepted as a floating-point literal expression. 5f32; }
请注意,例如 -1i8
被分析为两个 token:-
后跟 1i8
。
不作为文字表达式被接受的整数文字示例
#![allow(unused)] fn main() { #[cfg(FALSE)] { 0invalidSuffix; 123AFB43; 0b010a; 0xAB_CD_EF_GH; 0b1111_f32; } }
元组索引
词法分析器
TUPLE_INDEX
INTEGER_LITERAL
元组索引直接与文字 token 进行比较。元组索引从 0
开始,每个后续索引以十进制值将值递增 1
。因此,只有十进制值会匹配,并且该值不得有任何额外的 0
前缀字符。
#![allow(unused)] fn main() { let example = ("dog", "cat", "horse"); let dog = example.0; let cat = example.1; // The following examples are invalid. let cat = example.01; // ERROR no field named `01` let horse = example.0b10; // ERROR no field named `0b10` }
注意:元组索引可能包含某些后缀,但这并非旨在有效,并且可能会在未来的版本中删除。有关更多信息,请参阅 https://github.com/rust-lang/rust/issues/60210。
浮点数文字
词法分析器
FLOAT_LITERAL
DEC_LITERAL.
(不是紧跟.
,_
或 XID_Start 字符)
| DEC_LITERAL.
DEC_LITERAL SUFFIX_NO_E?
| DEC_LITERAL (.
DEC_LITERAL)? FLOAT_EXPONENT SUFFIX?FLOAT_EXPONENT
(e
|E
) (+
|-
)? (DEC_DIGIT|_
)* DEC_DIGIT (DEC_DIGIT|_
)*
浮点数文字 有两种形式之一
- 十进制文字 后跟句点字符
U+002E
(.
)。这可以可选地后跟另一个十进制文字,以及可选的指数。 - 单个十进制文字 后跟一个指数。
与整数文字类似,浮点数文字后面可以跟一个后缀,只要前缀部分不以 U+002E
(.
) 结尾。如果文字不包含指数,则后缀不能以 e
或 E
开头。有关这些后缀的效果,请参阅 浮点数文字表达式。
作为文字表达式被接受的浮点数文字示例
#![allow(unused)] fn main() { 123.0f64; 0.1f64; 0.1f32; 12E+99_f64; let x: f64 = 2.; }
最后一个示例有所不同,因为它无法将后缀语法与以句点结尾的浮点数文字 token 一起使用。2.f64
会尝试在 2
上调用名为 f64
的方法。
请注意,例如 -1.0
被分析为两个 token:-
后跟 1.0
。
不作为文字表达式被接受的浮点数文字示例
#![allow(unused)] fn main() { #[cfg(FALSE)] { 2.0f80; 2e5f80; 2e5e6; 2.0e5e6; 1.3e10u64; } }
类似于数字文字的保留形式
词法分析器
RESERVED_NUMBER
BIN_LITERAL [2
-9
]
| OCT_LITERAL [8
-9
]
| ( BIN_LITERAL | OCT_LITERAL | HEX_LITERAL ).
(不是紧跟.
,_
或 XID_Start 字符)
| ( BIN_LITERAL | OCT_LITERAL ) (e
|E
)
|0b
_
* 输入结束或不是 BIN_DIGIT
|0o
_
* 输入结束或不是 OCT_DIGIT
|0x
_
* 输入结束或不是 HEX_DIGIT
| DEC_LITERAL ( . DEC_LITERAL)? (e
|E
) (+
|-
)? 输入结束或不是 DEC_DIGIT
以下类似于数字文字的词法形式是保留形式。由于它们可能引起歧义,因此它们被 tokenizer 拒绝,而不是被解释为单独的 token。
- 一个没有后缀的二进制或八进制文字,后跟一个十进制数字,该数字超出其基数的范围,中间没有空格。
- 一个没有后缀的二进制、八进制或十六进制文字,后跟一个句点字符(对句点后面的内容的限制与浮点数文字相同),中间没有空格。
- 一个没有后缀的二进制或八进制文字,后跟字符
e
或E
,中间没有空格。
- 以基数前缀之一开头,但不是有效的二进制、八进制或十六进制文字(因为它不包含数字)的输入。
- 具有浮点数文字形式但指数中没有数字的输入。
保留形式示例
#![allow(unused)] fn main() { 0b0102; // this is not `0b010` followed by `2` 0o1279; // this is not `0o127` followed by `9` 0x80.0; // this is not `0x80` followed by `.` and `0` 0b101e; // this is not a suffixed literal, or `0b101` followed by `e` 0b; // this is not an integer literal, or `0` followed by `b` 0b_; // this is not an integer literal, or `0` followed by `b_` 2e; // this is not a floating-point literal, or `2` followed by `e` 2.0e; // this is not a floating-point literal, or `2.0` followed by `e` 2em; // this is not a suffixed literal, or `2` followed by `em` 2.0em; // this is not a suffixed literal, or `2.0` followed by `em` }
生命周期和循环标签
词法分析器
LIFETIME_TOKEN
'
IDENTIFIER_OR_KEYWORD (不是紧跟'
)
|'_
(不是紧跟'
)
| RAW_LIFETIMELIFETIME_OR_LABEL
'
NON_KEYWORD_IDENTIFIER (不是紧跟'
)
| RAW_LIFETIMERAW_LIFETIME
'r#
IDENTIFIER_OR_KEYWORD 除了crate
,self
,super
,Self
(不是紧跟'
)RESERVED_RAW_LIFETIME :
'r#_
(不是紧跟'
)
生命周期参数和 循环标签 使用 LIFETIME_OR_LABEL token。任何 LIFETIME_TOKEN 都将被词法分析器接受,例如,可以在宏中使用。
原始生命周期类似于普通生命周期,但其标识符以 r#
为前缀。(请注意,r#
前缀不包含在实际生命周期中。)
与普通生命周期不同,原始生命周期可以是任何严格或保留关键字,但 RAW_LIFETIME
上面列出的关键字除外。
为了避免与 占位符生命周期 混淆,使用 RESERVED_RAW_LIFETIME token 'r#_
是错误的。
版本差异:原始生命周期在 2021 版本或更高版本中被接受。在早期版本中,token
'r#lt
被词法分析为'r # lt
。
标点符号
标点符号 token 在此处列出是为了完整性。它们的各个用法和含义在链接的页面中定义。
符号 | 名称 | 用法 |
---|---|---|
+ | 加号 | 加法、Trait Bounds、Macro Kleene Matcher |
- | 减号 | 减法, 取反 |
* | 星号 | 乘法, 解引用, 裸指针, 宏 Kleene 匹配器, Use 通配符 |
/ | 斜杠 | 除法 |
% | 百分号 | 求余 |
^ | 脱字符 | 按位和逻辑异或 |
! | 非 | 按位和逻辑非, 宏调用, 内部属性, Never 类型, 负 impls |
& | 与 | 按位和逻辑与, 借用, 引用, 引用模式 |
| | 或 | 按位和逻辑或, 闭包, match 中的模式, if let, 和 while let |
&& | 与与 | 惰性与, 借用, 引用, 引用模式 |
|| | 或或 | 惰性或, 闭包 |
<< | 左移 | 左移, 嵌套泛型 |
>> | 右移 | 右移, 嵌套泛型 |
+= | 加等于 | 加法赋值 |
-= | 减等于 | 减法赋值 |
*= | 乘等于 | 乘法赋值 |
/= | 除等于 | 除法赋值 |
%= | 百分号等于 | 求余赋值 |
^= | 脱字符等于 | 按位异或赋值 |
&= | 与等于 | 按位与赋值 |
|= | 或等于 | 按位或赋值 |
<<= | 左移等于 | 左移赋值 |
>>= | 右移等于 | 右移赋值, 嵌套泛型 |
= | 等于 | 赋值, 属性, 各种类型定义 |
== | 等于等于 | 等于 |
!= | 不等 | 不等于 |
> | 大于 | 大于, 泛型, 路径 |
< | 小于 | 小于, 泛型, 路径 |
>= | 大于等于 | 大于或等于, 泛型 |
<= | 小于等于 | 小于或等于 |
@ | At | 子模式绑定 |
_ | 下划线 | 通配符模式, 推断类型, 常量中的未命名项, 外部 crate, use 声明, 和 解构赋值 |
. | 点 | 字段访问, 元组索引 |
.. | 点点 | 范围, 结构体表达式, 模式, 范围模式 |
... | 点点点 | 可变参数函数, 范围模式 |
..= | 点点等于 | 包含范围, 范围模式 |
, | 逗号 | 各种分隔符 |
; | 分号 | 各种项和语句的终止符, 数组类型 |
: | 冒号 | 各种分隔符 |
:: | 路径分隔符 | 路径分隔符 |
-> | 右箭头 | 函数返回类型, 闭包返回类型, 函数指针类型 |
=> | 胖箭头 | Match 分支, 宏 |
<- | 左箭头 | 左箭头符号自 Rust 1.0 之前就已不再使用,但它仍然被视为单个标记 |
# | 井号 | 属性 |
$ | 美元符号 | 宏 |
? | 问号 | 问号运算符, 可能有大小, 宏 Kleene 匹配器 |
~ | 波浪号 | 波浪号运算符自 Rust 1.0 之前就已不再使用,但其标记可能仍被使用 |
分隔符
括号标点符号用于语法的各个部分。左括号必须始终与右括号配对。括号及其中的标记在 宏 中被称为“标记树”。括号的三种类型是
括号 | 类型 |
---|---|
{ } | 花括号 |
[ ] | 方括号 |
( ) | 圆括号 |
保留前缀
词法分析器 2021+
RESERVED_TOKEN_DOUBLE_QUOTE : ( IDENTIFIER_OR_KEYWORD 除了b
或c
或r
或br
或cr
|_
)"
RESERVED_TOKEN_SINGLE_QUOTE : ( IDENTIFIER_OR_KEYWORD 除了b
|_
)'
RESERVED_TOKEN_POUND : ( IDENTIFIER_OR_KEYWORD 除了r
或br
或cr
|_
)#
RESERVED_TOKEN_LIFETIME :'
(IDENTIFIER_OR_KEYWORD 除了r
| _)#
一些词法形式被称为保留前缀,它们被保留供将来使用。
源输入在词法上会被解释为非原始标识符(或关键字或 _
),紧随其后是 #
、'
或 "
字符(没有中间的空白),这被识别为保留前缀。
请注意,原始标识符、原始字符串字面量和原始字节字符串字面量可能包含 #
字符,但不被解释为包含保留前缀。
类似地,原始字符串字面量、字节字面量、字节字符串字面量、原始字节字符串字面量、C 字符串字面量和原始 C 字符串字面量中使用的 r
、b
、br
、c
和 cr
前缀不被解释为保留前缀。
源输入在词法上会被解释为非原始生命周期(或关键字或 _
),紧随其后是 #
字符(没有中间的空白),这被识别为保留生命周期前缀。
版本差异:从 2021 版本开始,保留前缀被词法分析器报告为错误(特别是,它们不能传递给宏)。
在 2021 版本之前,保留前缀被词法分析器接受,并解释为多个标记(例如,一个标记用于标识符或关键字,后跟一个
#
标记)。所有版本都接受的示例
#![allow(unused)] fn main() { macro_rules! lexes {($($_:tt)*) => {}} lexes!{a #foo} lexes!{continue 'foo} lexes!{match "..." {}} lexes!{r#let#foo} // three tokens: r#let # foo lexes!{'prefix #lt} }
2021 版本之前接受但在之后被拒绝的示例
#![allow(unused)] fn main() { macro_rules! lexes {($($_:tt)*) => {}} lexes!{a#foo} lexes!{continue'foo} lexes!{match"..." {}} lexes!{'prefix#lt} }
保留 guard
词法分析器 2024+
RESERVED_GUARDED_STRING_LITERAL :#
+ STRING_LITERAL
RESERVED_POUNDS :#
2..
保留 guard 是为将来使用而保留的语法,如果使用将生成编译错误。
保留 guarded 字符串字面量是一个或多个 U+0023
(#
) 紧随其后的 STRING_LITERAL 的标记。
保留 pounds 是两个或多个 U+0023
(#
) 的标记。
版本差异:在 2024 版本之前,保留 guard 被词法分析器接受,并解释为多个标记。例如,
#"foo"#
形式被解释为三个标记。##
被解释为两个标记。