词法单元
Lexer
Token →
RESERVED_TOKEN
| RAW_IDENTIFIER
| CHAR_LITERAL
| STRING_LITERAL
| RAW_STRING_LITERAL
| BYTE_LITERAL
| BYTE_STRING_LITERAL
| RAW_BYTE_STRING_LITERAL
| C_STRING_LITERAL
| RAW_C_STRING_LITERAL
| FLOAT_LITERAL
| INTEGER_LITERAL
| LIFETIME_TOKEN
| PUNCTUATION
| IDENTIFIER_OR_KEYWORD
词法单元(Token)是由正则(非递归)语言定义的语法中的原始产物。Rust 源输入可以分解为以下类型的词法单元:
在本文档的语法中,“简单“词法单元以字符串表产物形式给出,并以 monospace 字体显示。
字面量
字面量是用于字面量表达式的词法单元。
示例
字符和字符串
| 示例 | # 集合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 |
ASCII 转义
| 名称 | |
|---|---|
\x41 | 7 位字符代码(恰好 2 个十六进制数字,最大 0x7F) |
\n | 换行 |
\r | 回车 |
\t | 制表符 |
\\ | 反斜杠 |
\0 | 空字符 |
字节转义
| 名称 | |
|---|---|
\x7F | 8 位字符代码(恰好 2 个十六进制数字) |
\n | 换行 |
\r | 回车 |
\t | 制表符 |
\\ | 反斜杠 |
\0 | 空字符 |
Unicode 转义
| 名称 | |
|---|---|
\u{7FFF} | 24 位 Unicode 字符代码(最多 6 个十六进制数字) |
引号转义
| 名称 | |
|---|---|
\' | 单引号 |
\" | 双引号 |
数字
后缀
后缀是跟在字面量主要部分之后(没有中间空白)的字符序列,形式与非原始标识符或关键字相同。
Lexer
SUFFIX →
_ ^ XID_Continue+
| XID_Start XID_Continue*
任何类型的字面量(字符串、整数等)带有任何后缀作为词法单元都是有效的。
带有任何后缀的字面量词法单元可以传递给宏而不会产生错误。宏本身将决定如何解释这样的词法单元以及是否产生错误。特别是,通过示例定义的宏的 literal 片段说明符匹配带有任意后缀的字面量词法单元。
#![allow(unused)]
fn main() {
macro_rules! blackhole { ($tt:tt) => () }
macro_rules! blackhole_lit { ($l:literal) => () }
blackhole!("string"suffix); // OK
blackhole_lit!(1suffix); // OK
}
然而,被解释为字面量表达式或模式的字面量词法单元上的后缀是受限的。非数字字面量词法单元上的任何后缀都会被拒绝,数字字面量词法单元只接受以下列表中的后缀。
| 整数 | 浮点数 |
|---|---|
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize | f32, f64 |
字符和字符串字面量
字符字面量
Lexer
CHAR_LITERAL →
'
( ~[' \ LF CR TAB] | 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..=6valid hex char value }3
字符字面量_是包含在两个 U+0027(单引号)字符内的单个 Unicode 字符,U+0027 本身除外,它必须通过前面的 U+005C 字符(\)进行_转义。
字符串字面量
Lexer
STRING_LITERAL →
" (
~[" \ CR]
| QUOTE_ESCAPE
| ASCII_ESCAPE
| UNICODE_ESCAPE
| STRING_CONTINUE
)* " SUFFIX?
STRING_CONTINUE → \ LF
字符串字面量_是包含在两个 U+0022(双引号)字符内的任意 Unicode 字符序列,U+0022 本身除外,它必须通过前面的 U+005C 字符(\)进行_转义。
换行符(由字符 U+000A (LF) 表示)允许在字符串字面量中使用。字符 U+000D (CR) 不得出现在字符串字面量中。当未转义的 U+005C 字符(\)紧接在换行符之前时,换行符不会出现在词法单元表示的字符串中。有关详细信息,请参阅字符串继续转义。
字符转义
在字符或非原始字符串字面量中可以使用一些额外的_转义_。转义以 U+005C(\)开头,后跟以下形式之一:
- 7 位代码点转义_以
U+0078(x)开头,后跟恰好两个_十六进制数字,值最大为0x7F。它表示值等于所提供十六进制值的 ASCII 字符。不允许更高的值,因为它们表示 Unicode 代码点还是字节值是不明确的。
- 24 位代码点转义_以
U+0075(u)开头,后跟最多六个_十六进制数字,用大括号U+007B({)和U+007D(})括起来。它表示等于所提供十六进制值的 Unicode 代码点。该值必须是有效的 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(\),必须进行转义才能表示其本身。
原始字符串字面量
Lexer
RAW_STRING_LITERAL →
r " ^ RAW_STRING_CONTENT " SUFFIX?
| r #n:1..=255 ^ " RAW_STRING_CONTENT_HASHED " #n SUFFIX?
RAW_STRING_CONTENT → ( !" ~CR )*
RAW_STRING_CONTENT_HASHED → ( !( " #n ) ~CR )*
原始字符串字面量不处理任何转义。它们以字符 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
}
字节和字节字符串字面量
字节字面量
Lexer
BYTE_LITERAL →
b' ^ ( ASCII_FOR_CHAR | BYTE_ESCAPE ) ' SUFFIX?
ASCII_FOR_CHAR → ![' \ LF CR TAB] ASCII
BYTE_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 位整数_数字字面量_。
字节字符串字面量
Lexer
BYTE_STRING_LITERAL →
b" ^ ( ASCII_FOR_STRING | BYTE_ESCAPE | STRING_CONTINUE )* " SUFFIX?
ASCII_FOR_STRING → ![" \ CR] ASCII
非原始_字节字符串字面量_是 ASCII 字符和_转义_的序列,前面是字符 U+0062(b)和 U+0022(双引号),后面是字符 U+0022。如果字符 U+0022 存在于字面量中,它必须通过前面的 U+005C(\)字符进行_转义_。或者,字节字符串字面量可以是下面定义的_原始字节字符串字面量_。
换行符(由字符 U+000A (LF) 表示)允许在字节字符串字面量中使用。字符 U+000D (CR) 不得出现在字节字符串字面量中。当未转义的 U+005C 字符(\)紧接在换行符之前时,换行符不会出现在词法单元表示的字符串中。有关详细信息,请参阅字符串继续转义。
在字节或非原始字节字符串字面量中可以使用一些额外的_转义_。转义以 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。
原始字节字符串字面量
Lexer
RAW_BYTE_STRING_LITERAL →
br " ^ RAW_BYTE_STRING_CONTENT " SUFFIX?
| br #n:1..=255 ^ " RAW_BYTE_STRING_CONTENT_HASHED " #n SUFFIX?
RAW_BYTE_STRING_CONTENT → ( !" ASCII_FOR_RAW )*
RAW_BYTE_STRING_CONTENT_HASHED → ( !( " #n ) ASCII_FOR_RAW )*
ASCII_FOR_RAW → !CR ASCII
原始字节字符串字面量不处理任何转义。它们以字符 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 字符串字面量
Lexer
C_STRING_LITERAL →
c" ^ (
~[" \ CR NUL]
| BYTE_ESCAPEexcept \0 or \x00
| UNICODE_ESCAPEexcept \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 字符串中使用。
换行符(由字符 U+000A (LF) 表示)允许在 C 字符串字面量中使用。字符 U+000D (CR) 不得出现在 C 字符串字面量中。当未转义的 U+005C 字符(\)紧接在换行符之前时,换行符不会出现在词法单元表示的字符串中。有关详细信息,请参阅字符串继续转义。
在非原始 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";
}
2021 Edition differences
C 字符串字面量在 2021 版本或更高版本中被接受。在早期版本中,词法单元
c""被词法分析为c ""。
原始 C 字符串字面量
Lexer
RAW_C_STRING_LITERAL →
cr " ^ RAW_C_STRING_CONTENT " SUFFIX?
| cr #n:1..=255 ^ " RAW_C_STRING_CONTENT_HASHED " #n SUFFIX?
RAW_C_STRING_CONTENT → ( !" ~[CR NUL] )*
RAW_C_STRING_CONTENT_HASHED → ( !( " #n ) ~[CR NUL] )*
原始 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(\)没有任何特殊含义。
2021 Edition differences
原始 C 字符串字面量在 2021 版本或更高版本中被接受。在早期版本中,词法单元
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
}
数字字面量
数字字面量_是_整数字面量_或_浮点数字面量。识别这两种字面量的语法是混合的。
整数字面量
Lexer
INTEGER_LITERAL →
( BIN_LITERAL | OCT_LITERAL | HEX_LITERAL | DEC_LITERAL )
^ !RESERVED_FLOAT SUFFIX?
DEC_LITERAL → DEC_DIGIT ( DEC_DIGIT | _ )*
BIN_LITERAL → 0b ^ _* BIN_DIGIT ( BIN_DIGIT | _ )* ![e E 2-9]
OCT_LITERAL → 0o ^ _* OCT_DIGIT ( OCT_DIGIT | _ )* ![e E 8-9]
HEX_LITERAL → 0x ^ _* HEX_DIGIT ( HEX_DIGIT | _ )*
BIN_DIGIT → [0-1]
OCT_DIGIT → [0-7]
DEC_DIGIT → [0-9]
HEX_DIGIT → [0-9 a-f A-F]
RESERVED_FLOAT → . !( . | _ | XID_Start )
_整数字面量_有四种形式之一:
- _十进制字面量_以十进制数字开头,后跟十进制数字和_下划线_的任意混合。
- _十六进制字面量_以字符序列
U+0030U+0078(0x)开头,后跟十六进制数字和下划线的任意混合(至少有一个数字)。
- _八进制字面量_以字符序列
U+0030U+006F(0o)开头,后跟八进制数字和下划线的任意混合(至少有一个数字)。
- _二进制字面量_以字符序列
U+0030U+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 被分析为两个词法单元:- 后跟 1i8。
不被接受为字面量表达式的整数字面量示例:
#![allow(unused)]
fn main() {
#[cfg(false)] {
0invalidSuffix;
123AFB43;
0b010a;
0xAB_CD_EF_GH;
0b1111_f32;
}
}
无效整数字面量
某些整数字面量形式是无效的。为避免歧义,词法分析器会拒绝它们,而不是将它们拆分为单独的词法单元。
#![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_`.
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`.
}
如果无后缀二进制或八进制字面量后面没有中间空白地跟着超出其基数范围的十进制数字,则是错误。
如果无后缀二进制、八进制或十六进制字面量后面没有中间空白地跟着句点字符(受与浮点数字面量中相同的关于句点后可跟内容的限制),则是错误。
如果无后缀二进制或八进制字面量后面没有中间空白地跟着字符 e 或 E,则是错误。
如果基数前缀后面(在任何可选的前导下划线之后)没有至少一个其基数的有效数字,则是错误。
元组索引
Lexer
TUPLE_INDEX → DEC_LITERAL | BIN_LITERAL | OCT_LITERAL | HEX_LITERAL
元组索引直接与字面量词法单元进行比较。元组索引以 0 开头,每个后续索引将值递增 1 作为十进制值。因此,只有十进制值会匹配,并且该值不能有任何额外的 0 前缀字符。
元组索引不能包含任何后缀(如 usize)。
#![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`
let unicorn = example.0usize; // ERROR suffixes on a tuple index are invalid
let underscore = example.0_0; // ERROR no field `0_0` on type `(&str, &str, &str)`
}
浮点数字面量
Lexer
FLOAT_LITERAL →
DEC_LITERAL ( . DEC_LITERAL )? FLOAT_EXPONENT SUFFIX?
| DEC_LITERAL . DEC_LITERAL SUFFIX?
| DEC_LITERAL . !( . | _ | XID_Start )
FLOAT_EXPONENT →
( e | E ) ^ ( + | - )? _* 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.;
}
最后一个例子不同,因为不能对以句点结尾的浮点字面量使用后缀语法。2.f64 将尝试在 2 上调用名为 f64 的方法。
请注意,例如 -1.0 被分析为两个词法单元:- 后跟 1.0。
不被接受为字面量表达式的浮点数字面量示例:
#![allow(unused)]
fn main() {
#[cfg(false)] {
2.0f80;
2e5f80;
2e5e6;
2.0e5e6;
1.3e10u64;
}
}
如果浮点数字面量的指数没有数字,则是错误。
#![allow(unused)]
fn main() {
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`.
}
生命周期和循环标签
Lexer
LIFETIME_TOKEN →
RAW_LIFETIME
| ' IDENTIFIER_OR_KEYWORD !'
LIFETIME_OR_LABEL →
RAW_LIFETIME
| ' NON_KEYWORD_IDENTIFIER !'
RAW_LIFETIME →
'r# ^ IDENTIFIER_OR_KEYWORD !'
RESERVED_RAW_LIFETIME → 'r# ( _ | crate | self | Self | super ) !( ' | XID_Continue )
生命周期参数和循环标签使用 LIFETIME_OR_LABEL 词法单元。词法分析器将接受任何 LIFETIME_TOKEN,例如可以在宏中使用。
原始生命周期类似于普通生命周期,但其标识符以 r# 为前缀。(请注意,r# 前缀不作为实际生命周期的一部分。)
与普通生命周期不同,原始生命周期可以是任何严格或保留关键字,除了上面为 RAW_LIFETIME 列出的那些。
使用 RESERVED_RAW_LIFETIME 词法单元是错误的。
2021 Edition differences
原始生命周期在 2021 版本或更高版本中被接受。在早期版本中,词法单元
'r#lt被词法分析为'r # lt。
标点符号
标点符号词法单元用作运算符、分隔符和语法的其他部分。
Lexer
PUNCTUATION →
...
| ..=
| <<=
| >>=
| !=
| %=
| &&
| &=
| *=
| +=
| -=
| ->
| ..
| /=
| ::
| <-
| <<
| <=
| ==
| =>
| >=
| >>
| ^=
| |=
| ||
| !
| #
| $
| %
| &
| (
| )
| *
| +
| ,
| -
| .
| /
| :
| ;
| <
| =
| >
| ?
| @
| [
| ]
| ^
| {
| |
| }
| ~
Note
有关标点符号字符的使用方式,请参阅语法索引。
分隔符
括号标点符号用于语法的各个部分。左括号必须始终与右括号配对。括号和其中的词法单元在宏中被称为“词法单元树“。三种类型的括号是:
| 括号 | 类型 |
|---|---|
{ } | 花括号 |
[ ] | 方括号 |
( ) | 圆括号 |
保留词法单元
几种词法单元形式被保留供将来使用或为了避免混淆。源输入匹配这些形式之一是错误的。
Lexer
RESERVED_TOKEN →
RESERVED_GUARDED_STRING_LITERAL
| RESERVED_POUNDS
| RESERVED_RAW_IDENTIFIER
| RESERVED_RAW_LIFETIME
| RESERVED_TOKEN_DOUBLE_QUOTE
| RESERVED_TOKEN_LIFETIME
| RESERVED_TOKEN_POUND
| RESERVED_TOKEN_SINGLE_QUOTE
保留前缀
Lexer
RESERVED_TOKEN_DOUBLE_QUOTE →
IDENTIFIER_OR_KEYWORDexcept b or c or r or br or cr "
RESERVED_TOKEN_SINGLE_QUOTE →
IDENTIFIER_OR_KEYWORDexcept b '
RESERVED_TOKEN_POUND →
IDENTIFIER_OR_KEYWORDexcept r or br or cr #
RESERVED_TOKEN_LIFETIME →
' IDENTIFIER_OR_KEYWORDexcept r #
一些被称为_保留前缀_的词法形式被保留供将来使用。
如果源输入在词法上被解释为非原始标识符(或关键字)并且紧接着 #、' 或 " 字符(没有中间空白),则被识别为保留前缀。
请注意,原始标识符、原始字符串字面量和原始字节字符串字面量可能包含 # 字符,但不被解释为包含保留前缀。
类似地,原始字符串字面量、字节字面量、字节字符串字面量、原始字节字符串字面量、C 字符串字面量和原始 C 字符串字面量中使用的 r、b、br、c 和 cr 前缀不被解释为保留前缀。
如果源输入在词法上被解释为非原始生命周期(或关键字)并且紧接着 # 字符(没有中间空白),则被识别为保留生命周期前缀。
2021 Edition differences
从 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} }
保留守卫
Lexer
RESERVED_GUARDED_STRING_LITERAL → #+ STRING_LITERAL
RESERVED_POUNDS → #2..
保留守卫是为将来使用保留的语法,如果使用将生成编译错误。
保留守卫字符串字面量是一个或多个 U+0023(#)后紧跟 STRING_LITERAL 的词法单元。
保留井号是两个或多个 U+0023(#)的词法单元。
2024 Edition differences
在 2024 版本之前,保留守卫被词法分析器接受并解释为多个词法单元。例如,
#"foo"#形式被解释为三个词法单元。##被解释为两个词法单元。