记号

tokens.md
commit d69a6d13c917856aa7da839647dcdbde6d9dde46

记号是语法中的主要组合,使用正则(非递归)语言定义。Rust 源输入可以被分解为下述类型的记号:

本书语法中,“简单”记号采用字符串表组合形式,并以等宽(monospace)字体显示。

字面量

字面量是一个包含单独记号(而不是一串记号)的表达式,它立即且直接的表示了它所赋的值,而不是通过名字或其它赋值规则引用它。字面量是常量表达式的一种形式,所以它(主要)在编译时赋值。

示例

字符和字符串

示例# 集合字符集转义
字符'H'0全部 Unicode引号 & ASCII & Unicode
字符串"hello"0全部 Unicode引号 & ASCII & Unicode
原生字符串r#"hello"#0 ...全部 UnicodeN/A
字节b'H'0全部 ASCII引号 & 字节
字节串b"hello"0全部 ASCII引号 & 字节
原生字节串br#"hello"#0 ...全部 ASCIIN/A

* 字面量两侧的 # 数量必须相同。

ASCII 转义

名称
\x417 位字符编码(2位,最大值为 0x7F
\n换行符
\r回车符
\t制表符
\\反斜线
\0Null/空/零值(译者注:Rust 中没有 Null)

字节转义

名称
\x7F8 位字符编码(2位)
\n换行符
\r回车符
\t制表符
\\反斜线
\0Null/空/零值(译者注:Rust 中没有 Null)

Unicode 转义

名称
\u{7FFF}24 位 Unicode 字符编码(最多6个数字)

引号转义

Name
\'单引号
\"双引号

数值

数字字面量*示例后缀
十进制整数98_222N/A整数后缀
十六进制整数0xffN/A整数后缀
二进制整数0o77N/A整数后缀
二进制整数0b1111_0000N/A整数后缀
浮点数123.0E+77Optional浮点数后缀

* 所有数字字面量允许 _ 作为可视分隔符:1_234.0E+18f64

后缀

后缀是紧跟(无空格)字面量主体部分之后的非原生标识符。

具有任意后缀的任何类型的字面量(如字符串、整数等)都是合法记号,可以传递给宏而不会产生错误。宏本身将决定如何诠释此类记号,以及是否产生错误。


#![allow(unused_variables)]
fn main() {
macro_rules! blackhole { ($tt:tt) => () }

blackhole!("string"suffix); // 没毛病 :-)
}

但是,解析为 Rust 代码的字面量记号,其后缀是受限制的。对于非数字字面量记号,将拒绝任何后缀;而对于数字字面量,仅接受具有下表中的后缀。

整型浮点型
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isizef32, f64

字符和字符串字面量

字符字面量

Lexer
CHAR_LITERAL :
   ' ( ~[' \ \n \r \t] | QUOTE_ESCAPE | ASCII_ESCAPE | UNICODE_ESCAPE ) '

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\)。

字符串字面量

Lexer
STRING_LITERAL :
   " (
      ~[" \ IsolatedCR]
      | QUOTE_ESCAPE
      | ASCII_ESCAPE
      | UNICODE_ESCAPE
      | STRING_CONTINUE
   )* "

STRING_CONTINUE :
   \ followed by \n

字符串字面量 是位于两个 U+0022(双引号)字符内的任意 Unicode 字符序列。当它是 U+0022 自身时,须前置 转义 字符 U+005C\)。

字符串字面量中允许分行。分行符可以换行符(U+000A),也可以是回车符和一对换行符(U+000D, U+000A)。此对字节序列通常转换为 U+000A,但有例外:当分行符前置一个未转义的 U+005C 字符(\)时,将会导致 U+005C 字符、换行符和下一行开头的所有空白都被忽略。是故下述示例中,ab 是等同的:


#![allow(unused_variables)]
fn main() {
let a = "foobar";
let b = "foo\
         bar";

assert_eq!(a,b);
}

字符转义

不管是字符字面量,还是非原生字符串字面量,都有一些额外 转义。一个转义以一个 U+005C\)开始,并后跟如下形式之一:

  • 7 位代码点转义U+0078x)开头,后紧跟两位 十六进制数字,最大值为 0x7F。表示其值等于它所提供的十六进制 ASCII 字符,但不能确定其是 Unicode 代码点还是字节值,所以更大的值是不被允许的。
  • 24 位代码点转义U+0075u)开头,后跟多达六位 十六进制数字,位于大括号 U+007B{)和 U+007D})之间。表示其值等于它所提供的十六进制 Unicode 代码点。
  • 空白转义 是字符 U+006En),U+0072r),或者 U+0074t)之一,依次表示 Unicode 值 U+000A(LF),U+000D(CR),或者 U+0009(HT)。
  • null/空/零值转义 是字符 U+00300),表示 Unicode 值 U+0000(NUL)。
  • 反斜杠转义 是字符 U+005C\),必须转义才能表示其自身。

原生字符串字面量

Lexer
RAW_STRING_LITERAL :
   r RAW_STRING_CONTENT

RAW_STRING_CONTENT :
      " ( ~ IsolatedCR )* (non-greedy) "
   | # RAW_STRING_CONTENT #

原生字符串字面量不处理任何转义。它以字符 U+0072r)后跟零个或多个字符 U+0023#),以及一个 U+0022(双引号)字符开始。原生字符串正文 可包含任意 Unicode 字符序列,并仅以另一个 U+0022(双引号)字符结尾,后跟与开头的 U+0022(双引号)字符前同等数量的 U+0023#)字符。

所有包含在原生字符串正文中的 Unicode 字符都代表他们自身,字符 U+0022(双引号)(当后跟的零个或多个 U+0023#)字符用于开始原生字符串字面量时除外)或 U+005C\)并无特殊含义。

字符串字面量示例:


#![allow(unused_variables)]
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 ) '

ASCII_FOR_CHAR :
   any ASCII (i.e. 0x00 to 0x7F), except ', \, \n, \r or \t

BYTE_ESCAPE :
      \x HEX_DIGIT HEX_DIGIT
   | \n | \r | \t | \\ | \0

字节字面量 是单个 ASCII 字符(在 U+0000U+007F 范围内)或单个 转义符,其前置字符 U+0062b)和 U+0027(单引号),后跟字符 U+0027。如果字面量中存在字符 U+0027,则必须由前置字符 U+005C\)转义,它相当于一个 u8(无符号 8 位整型)数字字面量

字节串字面量

Lexer
BYTE_STRING_LITERAL :
   b" ( ASCII_FOR_STRING | BYTE_ESCAPE | STRING_CONTINUE )* "

ASCII_FOR_STRING :
   any ASCII (i.e 0x00 to 0x7F), except ", \ and IsolatedCR

非原生 字节串字面量 是 ASCII 字符和 转义符,前置字符U+0062b)和 U+0022(双引号),以字符 U+0022 结尾。若字符 U+0022 出现在字节串字面量中,须由前置字符 U+005C\转义。或者,字节串字面量可以是 原生字节串字面量,定义为:长度为 n 的字节串字面量类型是 &'static [u8; n]

一些额外的 转义 可以在字节或非原生字节串字面量中使用,转义以 U+005C\)开始,并后跟如下形式之一:

  • 字节转义U+0078x)开始,后跟两个十六进制数,表示十六进制值代表的字节。
  • 空白转义 是字符 U+006En)、U+0072r),或 U+0074t)之一,分别表示字节值 0x0A(ASCII LF)、0x0D(ASCII CR),或 0x09(ASCII HT)。
  • null/空/零值转义 是字符 U+00300),表示字节值 U+0000(ASCII NUL)。
  • 反斜杠转义 是字符 U+005C\),必须被转义以表示其 ASCII 编码 0x5C

原生字节串字面量

Lexer
RAW_BYTE_STRING_LITERAL :
   br RAW_BYTE_STRING_CONTENT

RAW_BYTE_STRING_CONTENT :
      " ASCII* (non-greedy) "
   | # RAW_STRING_CONTENT #

ASCII :
   any ASCII (i.e. 0x00 to 0x7F)

原生字节串字面量不处理任何转义。它们以字符 U+0062b)开头,后跟 U+0072r),后跟零个或多个字符 U+0023#)及 U+0022(双引号)字符。原生字节串正文 可包含任意 ASCII 字符序列,并仅以另一个 U+0022(双引号)字符结尾,后面与开头 U+0022(双引号)字符之前同等数量的 U+0023#)字符。原生字节串字面量不能包含任何非 ASCII 字节。

原生字节串正文中的所有字符表示其 ASCII 编码,字符 U+0022(双引号)(当后跟的零个或多个 U+0023#)字符用于开始原生字节串字面量时除外)或 U+005C\)并无特殊含义。

字节串字面量示例:


#![allow(unused_variables)]
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
}

数字字面量

数字字面量 可以是 整型字面量,也可以是 浮点型字面量,此两类字面量的辨别语法是混合的。

整型字面量

Lexer
INTEGER_LITERAL :
   ( DEC_LITERAL | BIN_LITERAL | OCT_LITERAL | HEX_LITERAL ) INTEGER_SUFFIX?

DEC_LITERAL :
   DEC_DIGIT (DEC_DIGIT|_)*

TUPLE_INDEX :
      0    | NON_ZERO_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]

NON_ZERO_DEC_DIGIT : [1-9]

HEX_DIGIT : [0-9 a-f A-F]

INTEGER_SUFFIX :
      u8 | u16 | u32 | u64 | u128 | usize
   | i8 | i16 | i32 | i64 | i128 | isize

整型字面量具备下述 4 种形式之一:

  • 十进制字面量十进制数 开头,后跟 十进制数下划线 的任意组合。
  • 元组索引 可以是 0;也可以以 非零十进制数 开始,其后跟零个或多个十进制数。元组索引用于引用元组元组结构体,以及元组变量中的字段。
  • 十六进制字面量 以字符序列 U+0030 U+00780x)开头,后跟十六进制数和下划线的任意组合(至少一个数字)。
  • 八进制字面量 以字符序列 U+0030 U+006F0o)开头,后跟八进制数和下划线的任意组合(至少一个数字)。
  • 二进制字面量 以字符序列 U+0030 U+00620b)开头,后跟二进制数和下划线的任意组合(至少一个数字)。

如同其它字面量,整型字面量后面(紧跟,不带空白)可跟 整型后缀,该后缀强制设定了字面量的类型。整型后缀须为如下整型类型之一:u8i8u16i16u32i32u64i64u128i128usizeisize

无后缀 整型字面量的类型通过类型推断确定:

  • 如果整型类型可以通过程序上下文 唯一 确定,则无后缀整型字面量即为该类型。
  • 如果程序上下文对类型做了约束,则默认为有符号 32 位整型 i32
  • 如果程序上下文过度限制了类型,则将其视为静态类型错误。

不同形式的整型字面量示例:


#![allow(unused_variables)]
fn main() {
123;                               // type i32
123i32;                            // type i32
123u32;                            // type u32
123_u32;                           // type u32
let a: u64 = 123;                  // type u64

0xff;                              // type i32
0xff_u8;                           // type u8

0o70;                              // type i32
0o70_i16;                          // type i16

0b1111_1111_1001_0000;             // type i32
0b1111_1111_1001_0000i64;          // type i64
0b________1;                       // type i32

0usize;                            // type usize
}

无效整型字面量示例:


#![allow(unused_variables)]
fn main() {
// 无效后缀

0invalidSuffix;

// 错误进制

123AFB43;
0b0102;
0o0581;

// 类型溢出

128_i8;
256_u8;

// 至少需要一个数字(二进制、十六进制、八进制)

0b_;
0b____;
}

注意:Rust 语法将 -1i8 视作算术取负运算符对整型字面量 1i8 的应用,而非单个整型字面量。

浮点型字面量

Lexer
FLOAT_LITERAL :
      DEC_LITERAL . (not immediately followed by ., _ or an identifier)
   | DEC_LITERAL FLOAT_EXPONENT
   | DEC_LITERAL . DEC_LITERAL FLOAT_EXPONENT?
   | DEC_LITERAL (. DEC_LITERAL)? FLOAT_EXPONENT? FLOAT_SUFFIX

FLOAT_EXPONENT :
   (e|E) (+|-)? (DEC_DIGIT|_)* DEC_DIGIT (DEC_DIGIT|_)*

FLOAT_SUFFIX :
   f32 | f64

浮点型字面量具有如下两种形式之一:

  • 十进制字面量 后跟一个句点字符 U+002E.),之后可选后跟另一个十进制字面量和 指数
  • 单个 十进制字面量,后跟 指数

如同整型字面量,浮点型字面量也可后跟一个后缀,只要后缀之前部分不以 U+002E.)结尾。后缀强制设定了字面量类型。有两种有效的 浮点型后缀f32f64(32 位和 64 位浮点类型),它们显式地确定了字面量类型。

无后缀 浮点型字面量的类型通过类型推断确定:

  • 如果浮点型类型可以通过程序上下文 唯一 确定,则无后缀浮点型字面量即为该类型。
  • 如果程序上下文对类型做了约束,则默认为 f64
  • 如果程序上下文过度限制了类型,则将其视为静态类型错误。

不同形式的浮点型字面量示例:


#![allow(unused_variables)]
fn main() {
123.0f64;        // type f64
0.1f64;          // type f64
0.1f32;          // type f32
12E+99_f64;      // type f64
let x: f64 = 2.; // type f64
}

最后一个例子稍显不同,因为不能对一个以句点结尾的浮点型字面量使用后缀语法,2.f64 才会尝试在 2 上调用名为 f64 的方法。

浮点数所代表的语义在“机器类型”中描述。

布尔型字面量

Lexer
BOOLEAN_LITERAL :
      true
   | false

布尔类型有两个值:truefalse

生命周期

Lexer
LIFETIME_TOKEN :
      ' IDENTIFIER_OR_KEYWORD
   | '_

LIFETIME_OR_LABEL :
      ' NON_KEYWORD_IDENTIFIER

生命周期参数和循环标签 LIFETIME_OR_LABEL 记号。词法上接受任何 LIFETIME_TOKEN,比如在宏中使用。

运算符及符号

如下是 Rust 语言运算符的完整列表,它们各自的用法和意义在链接页面中定义。

SymbolNameUsage
+加/正号(Plus)算术加法复合类型限制宏匹配器
-减/负号(Minus)算术减法算术取负
*星号(Star)算术乘法解引用裸指针宏匹配器
/斜线(Slash)算术除法
%百分号(Percent)算术取模
^脱字符(Caret)按位异或
!叹号(Not)按位非、逻辑非宏调用(展开)内部属性never 型
&与(And)按位与、逻辑与借用引用引用模式
|或(Or)按位或、逻辑或闭包匹配
&&AndAndLazy 与借用引用引用模式
||OrOrLazy 或闭包
<<Shl左移嵌套泛型
>>Shr右移, 嵌套泛型
+=PlusEq算术加法及赋值
-=MinusEq算术减法及赋值
*=StarEq算术乘法及赋值
/=SlashEq算术除法及赋值
%=PercentEq算术取模及赋值
^=CaretEq按位异或及赋值
&=AndEq按位与及赋值
|=OrEq按位或及赋值
<<=ShlEq左移及赋值
>>=ShrEq右移及赋值嵌套泛型
=等号(Eq)赋值属性、类型定义
==EqEq等于
!=Ne不等于
>Gt大于泛型路径
<Lt小于泛型路径
>=Ge大于等于泛型
<=Le小于等于
@At模式绑定
_下划线(Underscore)通配符模式类型推导
.点号(Dot)成员访问元组索引
..DotDot范围结构体表达式模式
...DotDotDot变长参数函数范围模式
..=DotDotEq闭区间范围模式
,逗号(Comma)参数以及元素分隔符
;分号(Semi)各类项和语句结束符、数组类型
:Colon参数以及元素分隔符
::PathSep路径分隔符
->RArrow函数返回类型闭包返回类型
=>FatArrow匹配分支
#井号(Pound)属性
$美元符(Dollar)
?问号(Question)问号运算符, 不定大小, 宏匹配器

分隔符

括号用于语法的各个部分,且总是成对出现。括号及其内的记号在中被称作“记号树”。括号有三种类型:

括号类型
{ }花/大括号
[ ]方/中括号
( )圆/小括号