高级 The Rust Project Developers 著,rustwiki.org 中译 2026-09-13 14:59:08 · 0 阅读

第20章 附录

附录:关于宏随集的二义性的形式化规范

本文介绍了下述声明宏规则的正式规范。它们最初是在 RFC 550 中指定的(本文的大部分内容都是从其中复制过来的),并在后续的 RFC 中进行了进一步的展开。

定义和约定

  • macro:宏,源代码中任何可调用的类似 foo!(...) 的东西。
  • MBE:macro-by-example,声明宏,由 macro_rules 定义的宏。
  • matcher:匹配器,macro_rules调用中一条规则的左侧部分,或其子部分(subportion)。(译者注:子部分的意思是匹配器可嵌套,可相互包含)
  • macro parser:宏解释器,Rust 解析器中的一段程序,这段程序使用从所有命中的匹配器中推导出的文法规则来解析宏输入。
  • fragment:匹配段,给定匹配器将接受(或“匹配”)的 Rust 句法对象。
  • repetition :重复元,遵循正则重复模式的匹配段。
  • NT:non-terminal,非终结符,可以出现在匹配器中的各种“元变量”或重复元匹配器,在声明宏(MBE)句法中用前导字符 $ 标明。
  • simple NT:简单NT,“元变量”类型的非终结符(下面会进一步讨论)。
  • complex NT:复杂NT,重复元类型的非终结符,通过重复元操作符(\*, +, ?)指定重复次数。
  • token:匹配器中不可再细分的元素;例如,标识符、操作符、开/闭定界符简单NT(simple NT)。
  • token tree:token树,token树由 token(叶)、复杂NT 和子token树(token树的有限序列)组成的树形数据结构。
  • delimiter token:定界符,一种用于划分一个匹配段的结束和下一个匹配段的开始的 token。
  • separator token:分隔符,复杂NT 中的可选定界符,用在重复元里以分隔元素。
  • separated complex NT:带分隔符的复杂NT,分隔符是重复元的一部分的复杂NT。
  • delimited sequence:有界序列,在序列的开始和结束处使用了适当的开闭定界符的 token树。
  • empty fragment:空匹配段,一种不可见的 Rust 句法对象,它分割各种 token,例如空白符(whitespace)或者(在某些词法上下文中的)空标记序列。
  • fragment specifier:匹配段选择器,简单NT 中的后段标识符部分,指定 NT 接受哪种类型的匹配段。
  • language:与上下文无关的语言。

示例:


#![allow(unused)]
fn main() {
macro_rules! i_am_an_mbe {
    (start $foo:expr $($i:ident),* end) => ($foo)
}
}

(start $foo:expr $($i:ident),\* end) 是一个匹配器(matcher)。整个匹配器是一段有界字符序列(使用开闭定界符 () 界定),$foo$i 是简单NT(simple NT), exprident 是它们各自的匹配段选择器(fragment specifiers)。

$(i:ident),\* 是一个 NT;它是一个复杂NT,匹配那些被逗号分隔成的标识符类型的重复元。, 是这个复杂NT 的分隔符;它出现在匹配段的每对元素(如果有的话)之间。

复杂NT 的另一个例子是 $(hi $e:expr ;)+,它匹配 hi <expr>; hi <expr>; ... 这种格式的代码,其中 hi <expr>; 至少出现一次。注意,这种复杂NT 没有专用的分隔符。

(请注意,Rust 解析器会确保这类有界字符序列始终具有正确的 token树的嵌套结构以及开/闭定界符的正确匹配。)

下面,我们将用变量“M”表示匹配器,变量“t”和“u”表示任意单一 token,变量“tt”和“uu”表示任意 token树。(使用“tt”确实存在潜在的歧义,因为它的额外角色是一个匹配段选择器;但不用太担心,因为从上下文中,可以很清楚地看出哪个解释更符合语义)

用“SEP”代表分隔符,“OP”代表重复元运算符 \*, +, 和 ? “OPEN”/“CLOSE”代表包围定界字符序列的 token对(例如 [] )。

用希腊字母 "α" "β" "γ" "δ" 代表潜在的空token树序列。(注意没有使用希腊字母 "ε","ε"(epsilon)在此表示形式中代表一类特殊的角色,不代表 token树序列。)

  • 这种希腊字母约定通常只是在需要展现一段字符序列的技术细节时才被引入;特别是,当我们希望强调我们操作的是一个 token树序列时,我们将对该序列使用表义符 "tt ...",而不是一个希腊字母。

请注意,匹配器仅仅是一个 token树。如前所述,“简单NT”是一个元变量类型的 NT;因此,这是一个非重复元。例如,$foo:ty 是一个简单NT,而 $($foo:ty)+ 是一个复杂NT。

还请注意,在这种形式体系的上下文中,术语“token”通常包括简单NT。

最后,读者要记住,根据这种形式体系的定义,简单NT 不会匹配空匹配段,因此也没有 token 会匹配 Rust句法的空匹配段。(因此,能够匹配空匹配段的 NT 唯有复杂NT。)但这还不是全部事实,因为 vis 匹配器可以匹配空匹配段。因此,为了达到这种形式体系自洽统一的目的,我们将把 $v:vis 看作是 $($v:vis)?,来让匹配器匹配一个空匹配段。

匹配器的不变式

为了有效,匹配器必须满足以下三个不变式。注意其中 FIRST 和 FOLLOW 的定义将在后面进行描述。

  1. 对于匹配器 M 中的任意两个连续的 token树序列(即 M = ... tt uu ...),并且 uu ... 非空,必有 FOLLOW(... tt) ∪ {ε} ⊇ FIRST(uu ...)。
  2. 对于匹配器中任何带分隔符的复杂NT,M = ... $(tt ...) SEP OP ...,必有 SEP ∈ FOLLOW(tt ...)
  3. 对于匹配器中不带分隔符的复杂NT,M = ... $(tt ...) OP ...,如果 OP = \*+,必有 FOLLOW(tt ...) ⊇ FIRST(tt ...)。

第一个不变式表示,无论匹配器后出现什么 token(如果有的话),它都必须出现在先决随集(predetermined follow set)中的某个地方。这将确保合法的宏定义将继续对 ... tt 的结束和 uu ... 的开始执行相同的判定(determination),即使将来语言中添加了新的句法形式。 The first invariant says that whatever actual token that comes after a matcher, if any, must be somewhere in the predetermined follow set. This ensures that a legal macro definition will continue to assign the same determination as to where ... tt ends and uu ... begins, even as new syntactic forms are added to the language.

第二个不变式表示一个带分隔符的复杂NT 必须使用一个分隔符,它是 NT 的内部内容的先决随集的一部分。这将确保合法的宏定义将继续将输入匹配段解析成相同的定界字符序列 tt ...,即使在将来语言中添加了新的语法形式。 The second invariant says that a separated complex NT must use a separator token that is part of the predetermined follow set for the internal contents of the NT. This ensures that a legal macro definition will continue to parse an input fragment into the same delimited sequence of tt ...'s, even as new syntactic forms are added to the language.

第三个不变式说的是,当我们有一个复杂NT,它可以匹配同一字符序列的两个或多个副本,并且两者之间没有分隔符,那么根据第一个不变式,它们必须可以放在一起。这个不变式还要求它们是非空的,这消除了可能出现的歧义。 The third invariant says that when we have a complex NT that can match two or more copies of the same thing with no separation in between, it must be permissible for them to be placed next to each other as per the first invariant. This invariant also requires they be nonempty, which eliminates a possible ambiguity.

注意:由于历史疏忽和对行为的严重依赖,第三个不变式目前没有被执行。目前还没有决定下一步该怎么做。不遵循这个不变式的宏可能会在未来的 Rust版本中失效。参见跟踪问题 NOTE:The third invariant is currently unenforced due to historical oversight and significant reliance on the behaviour. It is currently undecided what to do about this going forward. Macros that do not respect the behaviour may become invalid in a future edition of Rust. See the tracking issue.

FIRST and FOLLOW, informally

非正式的 FIRST集合和 FOLLOW集合定义

给定匹配器 M 映射到三个集合:FIRST(M),LAST(M) 和 FOLLOW(M)。 A given matcher M maps to three sets:FIRST(M), LAST(M) and FOLLOW(M).

这三个集合中的每一个都是由一组 token 组成的。FIRST(M) 和 LAST(M) 也可能包含一个可区分的非token元素 ε ("epsilon"),这表示 M 可以匹配空匹配段。(但是 FOLLOW(M) 始终只是一组 token。) Each of the three sets is made up of tokens. FIRST(M) and LAST(M) may also contain a distinguished non-token element ε ("epsilon"), which indicates that M can match the empty fragment. (But FOLLOW(M) is always just a set of tokens.)

非正式定义(Informally):

  • FIRST(M):收集匹配段与 M 匹配时可能首先使用的 token。collects the tokens potentially used first when matching a fragment to M.

  • LAST(M):收集匹配段与 M 匹配时可能最后使用的 token。collects the tokens potentially used last when matching a fragment to M.

  • FOLLOW(M):允许紧跟在由 M 匹配的某个匹配段之后的 token集合。the set of tokens allowed to follow immediately after some fragment matched by M.

    换言之:t ∈ FOLLOW(M) 当且仅当存在(可能为空的)token序列 α、β、γ、δ,其中:

    • M 匹配 β,
    • t 与 γ 匹配,并且
    • 连结 α β γ δ 是一段可解析的 Rust程序。 In other words:t ∈ FOLLOW(M) if and only if there exists (potentially empty) token sequences α, β, γ, δ where:
    • M matches β,
    • t matches γ, and
    • The concatenation α β γ δ is a parseable Rust program.

我们使用简写的 ANYTOKEN 来表示所有 token(包括简单NT)的集合。例如,如果任何 token 在匹配器 M 之后都是合法的,那么 FOLLOW(M) = ANYTOKEN。 We use the shorthand ANYTOKEN to denote the set of all tokens (including simple NTs). For example, if any token is legal after a matcher M, then FOLLOW(M) = ANYTOKEN.

(为了加深对上述非正式定义描述的理解,读者在阅读正式定义之前,可以先在这里读一遍后面 关于 FIRST 和 LAST 的示例。) (To review one's understanding of the above informal descriptions, the reader at this point may want to jump ahead to the examples of FIRST/LAST before reading their formal definitions.)

FIRST, LAST

下面是对 FIRST 和 LAST 的正式归纳定义(formal inductive definitions)。

“A∪B”表示集合并集,“A∩B”表示集合交集,“A\B”表示集合差集(即存在于A中,且不存在于B中的所有元素的集合)。

FIRST

FIRST(M) 是通过对序列 M 及其第一个 token树(如果有的话)的结构进行案例分析来定义的: FIRST(M) is defined by case analysis on the sequence M and the structure of its first token-tree (if any):

  • 如果 M 为空序列,则 FIRST(M) = { ε },if M is the empty sequence, then FIRST(M) = { ε },

  • 如果 M 以 token t 开始,则 FIRST(M) = { t },if M starts with a token t, then FIRST(M) = { t },

    (注意:这涵盖了这样一种情况:M 以一个定界的token树序列开始,M = OPEN tt ... CLOSE ...,此时 t = OPEN,因此 FIRST(M) = { OPEN }。) (Note:this covers the case where M starts with a delimited token-tree sequence, M = OPEN tt ... CLOSE ..., in which case t = OPEN and thus FIRST(M) = { OPEN }.)

    (注意:这主要依赖于没有简单NT与空匹配段匹配这一特性。) (Note:this critically relies on the property that no simple NT matches the empty fragment.)

  • 否则,M 是一个以复杂NT开始的token树序列:M = $( tt ... ) OP α,或 M = $( tt ... ) SEP OP α,(其中 α 是匹配器其余部分的token树序列(可能是空的))。Otherwise, M is a token-tree sequence starting with a complex NT:M = $( tt ... ) OP α, or M = $( tt ... ) SEP OP α, (where α is the (potentially empty) sequence of token trees for the rest of the matcher).

    • Let SEP_SET(M) = { SEP } 如果存在 SEP 且 ε ∈ FIRST(tt ...);否则 SEP_SET(M) = {}。
  • Let ALPHA_SET(M) = FIRST(α) if OP = \* or ? and ALPHA_SET(M) = {} if OP = +.

  • FIRST(M) = (FIRST(tt ...) \ {ε}) ∪ SEP_SET(M) ∪ ALPHA_SET(M).

复杂NT 的定义值得商榷。SEP_SET(M) 定义了分隔符可能是 M 的第一个有效token的可能性,当定义了分隔符且重复匹配段可能为空时,就会发生这种情况。ALPHA_SET(M)定义了复杂NT可能为空的可能性,这意味着 M 的第一个有效token集合是后继token树序列 α 。当使用了操作符 \*? 时,这种情况下可能没有重复元。理论上,如果 + 与一个可能为空的重复匹配段一起使用,也会出现这种情况,但是第三个不变式禁止这样做。 The definition for complex NTs deserves some justification. SEP_SET(M) defines the possibility that the separator could be a valid first token for M, which happens when there is a separator defined and the repeated fragment could be empty. ALPHA_SET(M) defines the possibility that the complex NT could be empty, meaning that M's valid first tokens are those of the following token-tree sequences α. This occurs when either \* or ? is used, in which case there could be zero repetitions. In theory, this could also occur if + was used with a potentially-empty repeating fragment, but this is forbidden by the third invariant.

From there, clearly FIRST(M) can include any token from SEP_SET(M) or ALPHA_SET(M), and if the complex NT match is nonempty, then any token starting FIRST(tt ...) could work too. The last piece to consider is ε. SEP_SET(M) and FIRST(tt ...) \ {ε} cannot contain ε, but ALPHA_SET(M) could. Hence, this definition allows M to accept ε if and only if ε ∈ ALPHA_SET(M) does. This is correct because for M to accept ε in the complex NT case, both the complex NT and α must accept it. If OP = +, meaning that the complex NT cannot be empty, then by definition ε ∉ ALPHA_SET(M). Otherwise, the complex NT can accept zero repetitions, and then ALPHA_SET(M) = FOLLOW(α). So this definition is correct with respect to \varepsilon as well.

LAST

LAST(M), defined by case analysis on M itself (a sequence of token-trees):

  • if M is the empty sequence, then LAST(M) = { ε }

  • if M is a singleton token t, then LAST(M) = { t }

  • if M is the singleton complex NT repeating zero or more times, M = $( tt ... ) *, or M = $( tt ... ) SEP *

    • Let sep_set = { SEP } if SEP present; otherwise sep_set = {}.

    • if ε ∈ LAST(tt ...) then LAST(M) = LAST(tt ...) ∪ sep_set

    • otherwise, the sequence tt ... must be non-empty; LAST(M) = LAST(tt ...) ∪ {ε}.

  • if M is the singleton complex NT repeating one or more times, M = $( tt ... ) +, or M = $( tt ... ) SEP +

    • Let sep_set = { SEP } if SEP present; otherwise sep_set = {}.

    • if ε ∈ LAST(tt ...) then LAST(M) = LAST(tt ...) ∪ sep_set

    • otherwise, the sequence tt ... must be non-empty; LAST(M) = LAST(tt ...)

  • if M is the singleton complex NT repeating zero or one time, M = $( tt ...) ?, then LAST(M) = LAST(tt ...) ∪ {ε}.

  • if M is a delimited token-tree sequence OPEN tt ... CLOSE, then LAST(M) = { CLOSE }.

  • if M is a non-empty sequence of token-trees tt uu ...,

    • If ε ∈ LAST(uu ...), then LAST(M) = LAST(tt) ∪ (LAST(uu ...) \ { ε }).

    • Otherwise, the sequence uu ... must be non-empty; then LAST(M) = LAST(uu ...).

关于 FIRST 和 LAST 的示例

下面是一些关于 FIRST 和 LAST 的例子。(请特别注意,特殊元素 ε 是如何根据输入匹配段之间的相互作用来引入和消除的。) Below are some examples of FIRST and LAST. (Note in particular how the special ε element is introduced and eliminated based on the interaction between the pieces of the input.)

我们的第一个例子以树状结构呈现,以详细说明匹配器的分析是如何组成的。(一些较简单的子树已被删除。) Our first example is presented in a tree structure to elaborate on how the analysis of the matcher composes. (Some of the simpler subtrees have been elided.)

INPUT: $(  $d:ident   $e:expr   );*    $( $( h )* );*    $( f ; )+   g
            ~~~~~~~~   ~~~~~~~                ~
                |         |                   |
FIRST:  { $d:ident }  { $e:expr }          { h }


INPUT: $(  $d:ident   $e:expr   );*    $( $( h )* );*    $( f ; )+
            ~~~~~~~~~~~~~~~~~~             ~~~~~~~           ~~~
                        |                      |               |
FIRST:         { $d:ident }               { h, ε }         { f }

INPUT: $(  $d:ident   $e:expr   );*    $( $( h )* );*    $( f ; )+   g
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~    ~~~~~~~~~~~~~~    ~~~~~~~~~   ~
                        |                       |              |       |
FIRST:       { $d:ident, ε }            {  h, ε, ;  }      { f }   { g }


INPUT: $(  $d:ident   $e:expr   );*    $( $( h )* );*    $( f ; )+   g
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                        |
FIRST:                      { $d:ident, h, ;,  f }

Thus:

  • FIRST($($d:ident $e:expr );* $( $(h)* );* $( f ;)+ g) = { $d:ident, h, ;, f }

Note however that:

  • FIRST($($d:ident $e:expr );* $( $(h)* );* $($( f ;)+ g)*) = { $d:ident, h, ;, f, ε }

Here are similar examples but now for LAST.

  • LAST($d:ident $e:expr) = { $e:expr }
  • LAST($( $d:ident $e:expr );*) = { $e:expr, ε }
  • LAST($( $d:ident $e:expr );* $(h)*) = { $e:expr, ε, h }
  • LAST($( $d:ident $e:expr );* $(h)* $( f ;)+) = { ; }
  • LAST($( $d:ident $e:expr );* $(h)* $( f ;)+ g) = { g }

FOLLOW(M)

Finally, the definition for FOLLOW(M) is built up as follows. pat, expr, etc. represent simple nonterminals with the given fragment specifier.

  • FOLLOW(pat) = {=>, ,, =, |, if, in}`.

  • FOLLOW(expr) = FOLLOW(stmt) = {=>, ,, ;}`.

  • FOLLOW(ty) = FOLLOW(path) = {{, [, ,, =>, :, =, >, >>, ;, |, as, where, block nonterminals}.

  • FOLLOW(vis) = {,l any keyword or identifier except a non-raw priv; any token that can begin a type; ident, ty, and path nonterminals}.

  • FOLLOW(t) = ANYTOKEN for any other simple token, including block, ident, tt, item, lifetime, literal and meta simple nonterminals, and all terminals.

  • FOLLOW(M), for any other M, is defined as the intersection, as t ranges over (LAST(M) \ {ε}), of FOLLOW(t).

The tokens that can begin a type are, as of this writing, {(, [, !, \*, &, &&, ?, lifetimes, >, >>, ::, any non-keyword identifier, super, self, Self, extern, crate, $crate, _, for, impl, fn, unsafe, typeof, dyn}, although this list may not be complete because people won't always remember to update the appendix when new ones are added.

Examples of FOLLOW for complex M:

  • FOLLOW($( $d:ident $e:expr )\*) = FOLLOW($e:expr)
  • FOLLOW($( $d:ident $e:expr )\* $(;)\*) = FOLLOW($e:expr) ∩ ANYTOKEN = FOLLOW($e:expr)
  • FOLLOW($( $d:ident $e:expr )\* $(;)\* $( f |)+) = ANYTOKEN

Examples of valid and invalid matchers

With the above specification in hand, we can present arguments for why particular matchers are legal and others are not.

  • ($ty:ty < foo ,) :illegal, because FIRST(< foo ,) = { < } ⊈ FOLLOW(ty)

  • ($ty:ty , foo <) :legal, because FIRST(, foo <) = { , } is ⊆ FOLLOW(ty).

  • ($pa:pat $pb:pat $ty:ty ,) :illegal, because FIRST($pb:pat $ty:ty ,) = { $pb:pat } ⊈ FOLLOW(pat), and also FIRST($ty:ty ,) = { $ty:ty } ⊈ FOLLOW(pat).

  • ( $($a:tt $b:tt)* ; ) :legal, because FIRST($b:tt) = { $b:tt } is ⊆ FOLLOW(tt) = ANYTOKEN, as is FIRST(;) = { ; }.

  • ( $($t:tt),* , $(t:tt),* ) :legal, (though any attempt to actually use this macro will signal a local ambiguity error during expansion).

  • ($ty:ty $(; not sep)* -) :illegal, because FIRST($(; not sep)* -) = { ;, - } is not in FOLLOW(ty).

  • ($($ty:ty)-+) :illegal, because separator - is not in FOLLOW(ty).

  • ($($e:expr)*) :illegal, because expr NTs are not in FOLLOW(expr NT).

影响来源

Rust 并不是一种极端原创的语言,它的设计元素来源广泛。下面列出了其中一些(包括已经删除的):

  • SML,OCaml:代数数据类型、模式匹配、类型推断、分号语句分隔
  • C++:引用,RAII,智能指针,移动语义,单态化(monomorphization),内存模型
  • ML Kit, Cyclone:基于区域的内存管理(region based memory management)
  • Haskell (GHC):类型属性(typeclasses), 类型簇(type families)
  • Newsqueak, Alef, Limbo:通道,并发
  • Erlang:消息传递,线程失败,链接线程失败轻量级并发
  • Swift:可选绑定(optional bindings)
  • Scheme:卫生宏(hygienic macros)
  • C#:属性
  • Ruby:闭包句法,块句法
  • NIL, Hermes:typestate
  • Unicode Annex #31:标识符和模式句法

术语表

抽象句法树

“抽象句法树”,或“AST”,是编译器编译程序时,程序结构的中间表示形式。
An ‘abstract syntax tree’, or ‘AST’, is an intermediate representation of the structure of the program when the compiler is compiling it.

对齐量

值的对齐量指定值的首选起始存储地址。对齐量总是2的幂次。值的引用必须是对齐的。更多
The alignment of a value specifies what addresses values are preferred to start at. Always a power of two. References to a value must be aligned. More.

元数

元数是指函数或运算符接受的参数个数。例如,f(2, 3)g(4, 6) 的元数为2,而 h(8, 2, 6) 的元数为3。 ! 运算符的元数为1。
Arity refers to the number of arguments a function or operator takes. For some examples, f(2, 3) and g(4, 6) have arity 2, while h(8, 2, 6) has arity 3. The ! operator has arity 1.

数组

数组,有时也称为固定大小数组或内联数组,是描述关于元素集合的值,每个元素都可由程序在运行时计算的索引选择。内存模型上,数组占用连续的内存区域。
An array, sometimes also called a fixed-size array or an inline array, is a value describing a collection of elements, each selected by an index that can be computed at run time by the program. It occupies a contiguous region of memory.

关联程序项/关联项

关联程序项是与另一个程序项关联的程序项。关联程序项在 trait 中声明,在实现中定义。只有函数、常量和类型别名可以作为关联程序项。它与自由程序项形成对比。
An associated item is an item that is associated with another item. Associated items are defined in implementations and declared in traits. Only functions, constants, and type aliases can be associated. Contrast to a free item.

blanket实现

指为无覆盖类型实现的任何实现。impl<T> Foo for Timpl<T> Bar<T> for Timpl<T> Bar<Vec<T>> for T、 和 impl<T> Bar<T> for Vec<T> 被认为是 blanket实现。但是,impl<T> Bar<Vec<T>> for Vec<T> 不被认为是,因为这个 impl 中所有的 T 的实例都被 Vec 覆盖。
Any implementation where a type appears uncovered. impl<T> Foo for T, impl<T> Bar<T> for T, impl<T> Bar<Vec<T>> for T, and impl<T> Bar<T> for Vec<T> are considered blanket impls. However, impl<T> Bar<Vec<T>> for Vec<T> is not a blanket impl, as all instances of T which appear in this impl are covered by Vec.

约束

约束是对类型或 trait 的限制。例如,如果在函数的形数上设置了约束,则传递给该函数的实参的类型必须遵守该约束。
Bounds are constraints on a type or trait. For example, if a bound is placed on the argument a function takes, types passed to that function must abide by that constraint.

组合算子

组合子是高阶函数,它的参数全是函数或之前定义的组合子。组合子利用这些函数或组合子返回的结果作为入参进行进一步的逻辑计算和输出。组合子可用于以模块化的方式管理控制流。
Combinators are higher-order functions that apply only functions and earlier defined combinators to provide a result from its arguments. They can be used to manage control flow in a modular fashion.

分发

分发是一种机制,用于确定涉及到多态性时实际运行的是哪个版本的代码。分发的两种主要形式是静态分发和动态分发。虽然 Rust 支持静态分发,但它也通过一种称为 trait对象的机制支持动态分发。
Dispatch is the mechanism to determine which specific version of code is actually run when it involves polymorphism. Two major forms of dispatch are static dispatch and dynamic dispatch. While Rust favors static dispatch, it also supports dynamic dispatch through a mechanism called ‘trait objects’.

动态尺寸类型

动态尺寸类型(DST)是一种没有静态已知尺寸或对齐量的类型。
A dynamically sized type (DST) is a type without a statically known size or alignment.

实体

实体(entity)是一种语言结构,在源程序中可以以某种方式被引用,通常是通过路径(path)。实体包括类型程序项泛型参数变量绑定循环标签生存期字段属性lints
An entity is a language construct that can be referred to in some way within the source program, usually via a path. Entities include types, items, generic parameters, variable bindings, loop labels, lifetimes, fields, attributes, and lints.

表达式

表达式是值、常量、变量、运算符/操作符和函数的组合,计算/求值结果为单个值,有或没有副作用都有可能。
比如,2 + (3 * 4) 是一个返回值为14的表达式。
An expression is a combination of values, constants, variables, operators and functions that evaluate to a single value, with or without side-effects.
For example, 2 + (3 * 4) is an expression that returns the value 14.

自由程序项

不是任何实现的成员的程序项,如自由函数自由常量。自由程序项是与关联程序项相对的概念。
An item that is not a member of an implementation, such as a free function or a free const. Contrast to an associated item.

基础性trait

基础性trait 就是如果为现有的类型实现它,就会为该类型带来突破性改变的 trait。比如 FnSized 就是基础性trait。
A fundamental trait is one where adding an impl of it for an existing type is a breaking change. The Fn traits and Sized are fundamental.

基本类型构造器

基本类型构造器是这样一种类型,在它之上实现一个 blanket实现是一个突破性的改变。&&mutBox、和 Pin 是基本类型构造器。
如果任何时候 T 都被认为是本地类型,那 &T&mut TBox<T>、和 Pin<T> 也被认为是本地类型。基本类型构造器不能覆盖其他类型。任何时候使用术语“有覆盖类型”时,都默认把&T&mut TBox<T>、和Pin<T> 排除在外。
A fundamental type constructor is a type where implementing a blanket implementation over it is a breaking change. &, &mut, Box, and Pin are fundamental.
Any time a type T is considered local, &T, &mut T, Box<T>, and Pin<T> are also considered local. Fundamental type constructors cannot cover other types. Any time the term "covered type" is used, the T in &T, &mut T, Box<T>, and Pin<T> is not considered covered.

Inhabited

如果类型具有构造函数,因此可以实例化,则该类型是 inhabited。inhabited 类型不是“空的”,因为可以有类型对应的值。与之相对的是 Uninhabited
A type is inhabited if it has constructors and therefore can be instantiated. An inhabited type is not "empty" in the sense that there can be values of the type. Opposite of Uninhabited.

固有实现

单独标称类型上的实现 ,注意关键字 impl 后直接是标称类型,而非 trait-标称类型对(trait-type pair)上的实现。更多
An implementation that applies to a nominal type, not to a trait-type pair. More.

固有方法

固有实现中而不是在 trait实现中定义的方法
A method defined in an inherent implementation, not in a trait implementation.

初始化

如果一个变量已经被分配了一个值,并且此值还没有被移动走,那此变量就被初始化了。对此变量而言,它会假设它之外的所有其他内存位置都未初始化。只有非安全Rust 可以在不初始化的情况下开辟内存新区域。
A variable is initialized if it has been assigned a value and hasn't since been moved from. All other memory locations are assumed to be uninitialized. Only unsafe Rust can create such a memory without initializing it.

本地 trait

本地 trait 是在当前 crate 中定义的 trait。它可以在模块局部定义,也可以是依附于其他类型参数而定义。给定 trait Foo<T, U>Foo 总是本地的,不管替代 TU 的类型是什么。
A trait which was defined in the current crate. A trait definition is local or not independent of applied type arguments. Given trait Foo<T, U>, Foo is always local, regardless of the types substituted for T and U.

Turbofish

表达式中带有泛型参数的路径必须在左尖括号前加上一个 ::。 这种为表达泛型而结合起来形式(::<>)看起来有些像一条鱼。 因此,在口头上就被称为 turbofish句法。
Paths with generic parameters in expressions must prefix the opening brackets with a ::. Combined with the angular brackets for generics, this looks like a fish ::<>. As such, this syntax is colloquially referred to as turbofish syntax.

例如:


#![allow(unused)]
fn main() {
let ok_num = Ok::<_, ()>(5);
let vec = [1, 2, 3].iter().map(|n| n * 2).collect::<Vec<_>>();
}

这里必须使用 ::前缀,以便在逗号分隔的列表中进行多次比较时消除泛型路径可能的歧义。 参见 the bastion of the turbofish 中因为没有此前缀的而引起歧义的示例。
This :: prefix is required to disambiguate generic paths with multiple comparisons in a comma-separate list. See the bastion of the turbofish for an example where not having the prefix would be ambiguous.

本地类型

指在当前 crate 中定义的 structenum、或 union 。本地类型不会受到类型参数的影响。struct Foo 被认为是本地的,但 Vec<Foo> 不是。LocalType<ForeignType> 是本地的。类型别名不影响本地性。
A struct, enum, or union which was defined in the current crate. This is not affected by applied type arguments. struct Foo is considered local, but Vec<Foo> is not. LocalType<ForeignType> is local. Type aliases do not affect locality.

名称

名称是一个指向实体标识符生存期或循环标签。*名称绑定(name binding)*是指实体声明时引入了与该实体相关联的标识符或标签。路径、标识符和标签用于引用实体。
A name is an identifier or lifetime or loop label that refers to an entity. A name binding is when an entity declaration introduces an identifier or label associated with that entity. Paths, identifiers, and labels are used to refer to an entity.

名称解析

名称解析是将路径标识符标签实体(entity)声明绑定在一起的的编译过程。
Name resolution is the compile-time process of tying paths, identifiers, and labels to entity declarations.

命名空间

命名空间是基于名称所引用的实体类型的声明名称的逻辑分组。命名空间让在一个命名空间中出现的名称不会与另一个命名空间中的相同名称冲突。
A namespace is a logical grouping of declared names based on the kind of entity the name refers to. Namespaces allow the occurrence of a name in one namespace to not conflict with the same name in another namespace.
在命名空间中,名称统一组织在一个层次结构中,层次结构的每一层都有自己的命名实体集合。
Within a namespace, names are organized in a hierarchy, where each level of the hierarchy has its own collection of named entities.

标称类型

可用路径直接引用的类型。具体来说就是枚举(enum)结构体(struct)联合体(union)trait对象
Types that can be referred to by a path directly. Specifically enums, structs, unions, and trait objects.

对象安全 trait

可以用作 trait对象trait。只有遵循特定规则的 trait 才是对象安全的。
Traits that can be used as trait objects. Only traits that follow specific rules are object safe.

路径

路径是一个或多个路径段组成的序列,用于引用当前作用域或某命名空间层次结构中的实体
A path is a sequence of one or more path segments used to refer to an entity in the current scope or other levels of a namespace hierarchy.

预加载模块集/预导入包

预加载模块集,或者 Rust 预加载模块集,是一个会被导入到每个 crate 中的每个模块的小型程序项集合(其中大部分是 trait)。trait 在预加载模块集中很普遍。
Prelude, or The Rust Prelude, is a small collection of items - mostly traits - that are imported into every module of every crate. The traits in the prelude are pervasive.

作用域

作用域是源文本的一个区域,在该区域中可以直接使用其名称来引用在其下命名的命名实体
A scope is the region of source text where a named entity may be referenced with that name.

检验对象\检验对象表达式

检验对象是在匹配(match)表达式和类似的模式匹配结构上匹配的表达式。例如,在 match x { A => 1, B => 2 } 中,表达式 x 是 scrutinee。
A scrutinee is the expression that is matched on in match expressions and similar pattern matching constructs. For example, in match x { A => 1, B => 2 }, the expression x is the scrutinee.

类型尺寸/尺寸

值的尺寸有两个定义。
第一个是必须分配多少内存来存储这个值。
第二个是它是在具有该项类型的数组中连续元素之间的字节偏移量。
它是对齐量的整数倍数,包括零倍。尺寸会根据编译器版本(进行新的优化时)和目标平台(类似于 usize 在不同平台上的变化)而变化。
查看更多. The size of a value has two definitions.
The first is that it is how much memory must be allocated to store that value.
The second is that it is the offset in bytes between successive elements in an array with that item type. It is a multiple of the alignment, including zero. The size can change depending on compiler version (as new optimizations are made) and target platform (similar to how usize varies per-platform).

切片

切片是一段连续的内存序列上的具有动态尺寸视图功能的类型,写为 [T]
它经常以借用的形式出现,可变借用和共享借用都有可能。共享借用切片类型是 &[T],可变借用切片类型是 &mut [T],其中 T 表示元素类型。
A slice is dynamically-sized view into a contiguous sequence, written as [T].
It is often seen in its borrowed forms, either mutable or shared. The shared slice type is &[T], while the mutable slice type is &mut [T], where T represents the element type.

语句

语句是编程语言中最小的独立元素,它命令计算机执行一个动作。
A statement is the smallest standalone element of a programming language that commands a computer to perform an action.

字符串字面量

字符串字面量是直接存储在最终二进制文件中的字符串,因此在 'static 生存期内是有效的。
它的类型是借用形式的生存期 'static 的字符串切片,即:&'static str
A string literal is a string stored directly in the final binary, and so will be valid for the 'static duration.
Its type is 'static duration borrowed string slice, &'static str.

字符串切片(str)

字符串切片是 Rust 中最基础的字符串类型,写为 str。它经常以借用的形式出现,可变借用和共享借用都有可能。共享借用的字符串切片类型是 &str,可变借用的字符串切片类型是 &mut str
字符串切片总是有效的 UTF-8。
A string slice is the most primitive string type in Rust, written as str. It is often seen in its borrowed forms, either mutable or shared. The shared string slice type is &str, while the mutable string slice type is &mut str.
Strings slices are always valid UTF-8.

Trait

trait 是一种程序项,用于描述类型必须提供的功能。它允许类型对其行为做出某些承诺。
泛型函数和泛型结构体可以使用 trait 来限制或约束它们所接受的类型。
A trait is a language item that is used for describing the functionalities a type must provide. It allows a type to make certain promises about its behavior.
Generic functions and generic structs can use traits to constrain, or bound, the types they accept.

无覆盖类型

不作为其他类型的参数出现的类型。例如,T 就是无覆盖的,但 Vec<T> 中的 T 就是有覆盖的。这(种说法)只与类型参数相关。
A type which does not appear as an argument to another type. For example, T is uncovered, but the T in Vec<T> is covered. This is only relevant for type arguments.

未定义行为

非指定的编译时或运行时行为。这可能导致,但不限于:进程终止或崩溃;不正确的、不正确的或非预定计算;或特定于平台的结果。查看更多
Compile-time or run-time behavior that is not specified. This may result in, but is not limited to: process termination or corruption; improper, incorrect, or unintended computation; or platform-specific results. More.

Uninhabited

如果类型没有构造函数,因此永远不能实例化,则该类型是 Uninhabited。一个 Uninhabited 类型是“空的”,意思是该类型没有值。Uninhabited 类型的典型例子是 never type !,或不带变体的 enum Never { }。与之相对的是 Inhabited
A type is uninhabited if it has no constructors and therefore can never be instantiated. An uninhabited type is "empty" in the sense that there are no values of the type. The canonical example of an uninhabited type is the never type !, or an enum with no variants enum Never { }. Opposite of Inhabited.

上一篇
第19章 Rust运行时

本篇用到的工具

Rust
Rust
Rust 是一种注重性能、内存安全与并发的系统编程语言。其所有权系统在编译期检查内存访问,无需垃圾回收即可保证内存安全,并把大量并发错误拦截在编译阶段;凭借接近 C/C++ 的运行性能与零成本抽象,适用于操作系统、数据库、网络服务、命令行工具、WebAssembly 与嵌入式开发等场景。项目由 rust-lang 社区维护,官方包管理与构建工具为 cargo。

评论 (0)