箱和源文件
注意:虽然 Rust 和任何其他语言一样,可以使用解释器以及编译器来实现,但目前唯一的实现是编译器,并且该语言一直被设计为编译型语言。 由于这些原因,本节假设使用编译器。
Rust 的语义遵循编译时和运行时的阶段区分。1 具有静态解释的语义规则控制编译的成功或失败,而具有动态解释的语义规则控制程序在运行时的行为。
编译模型以称为箱(crates)的工件为中心。 每次编译处理单个源格式的箱,如果成功,则生成单个二进制格式的箱:可执行文件或某种库。2
箱是编译和链接的单元,以及版本控制、分发和运行时加载的单元。 一个箱包含一个嵌套的 模块 作用域树。 该树的顶层是一个匿名的模块(从模块内路径的角度来看),并且箱内的任何项都具有规范的 模块路径,表示其在箱的模块树中的位置。
Rust 编译器始终使用单个源文件作为输入来调用,并且始终生成单个输出箱。 源文件的处理可能会导致其他源文件作为模块加载。 源文件具有扩展名 .rs
。
Rust 源文件描述一个模块,其名称和位置(在当前箱的模块树中)是从源文件外部定义的:可以通过引用源文件中的显式 模块 项来定义,也可以通过箱本身的名称来定义。
每个源文件都是一个模块,但并非每个模块都需要自己的源文件:模块定义可以嵌套在一个文件中。
每个源文件包含零个或多个 项 定义的序列,并且可以选择性地以应用于包含模块的任意数量的 属性 开始,其中大多数属性会影响编译器的行为。
匿名箱模块可以具有应用于整个箱的附加属性。
注意:文件的内容前面可能有一个 shebang。
#![allow(unused)] fn main() { // Specify the crate name. #![crate_name = "projx"] // Specify the type of output artifact. #![crate_type = "lib"] // Turn on a warning. // This can be done in any module, not just the anonymous crate module. #![warn(non_camel_case_types)] }
主函数
包含 main
函数 的箱可以编译为可执行文件。
如果存在 main
函数,则它必须不接受任何参数,不得声明任何 trait 或生命周期约束,不得有任何 where 子句,并且其返回类型必须实现 Termination
trait。
fn main() {}
fn main() -> ! { std::process::exit(0); }
fn main() -> impl std::process::Termination { std::process::ExitCode::SUCCESS }
main
函数可以是导入的,例如,从外部箱或当前箱导入。
#![allow(unused)] fn main() { mod foo { pub fn bar() { println!("Hello, world!"); } } use foo::bar as main; }
注意:标准库中实现了
Termination
的类型包括
()
!
Infallible
ExitCode
Result<T, E>,其中 T: Termination, E: Debug
no_main
属性
no_main
属性 可以应用于箱级别,以禁用为可执行二进制文件发出 main
符号。 当要链接到的某些其他对象定义了 main
时,这很有用。
crate_name
属性
crate_name
属性 可以应用于箱级别,以使用 MetaNameValueStr 语法指定箱的名称。
#![allow(unused)] #![crate_name = "mycrate"] fn main() { }
箱名称不能为空,并且必须仅包含 Unicode 字母数字 或 _
(U+005F) 字符。
这种区分也存在于解释器中。 诸如语法分析、类型检查和 lints 等静态检查应该在程序执行之前发生,无论它何时执行。
箱在某种程度上类似于 ECMA-335 CLI 模型中的 assembly,SML/NJ Compilation Manager 中的 library,Owens 和 Flatt 模块系统中的 unit,或 Mesa 中的 configuration。