crate #
一个 package 包含一个或多个 crate, crate 分 3 种类型: binary、library、procedure_macro。
使用 cargo new my-project 命令创建 package,一个 package 至多包含一个 library crate(使用 --lib 参数来指定), 可以包含任意数量的 binary crate(默认,也可以使用 --bin 参数来指定)。
# 创建 bin crate(默认), 名为 foo
cargo new foo
# 创建 lib crate,名为 bar
cargo new --lib bar
crate 是 Rust 的编译、发布、版本化、加载的单元, crate 由一系列可以嵌套的 module 组成,其中 crate root module 是 Rust 编译器开始编译的根文件。
src/lib.rs: library crate 的 crate root,crate 名称与 package 名相同;src/main.rs: binary crate 的 crate root,crate 名称与 package 名相同;src/bin/: 下面每个文件对应一个 binary crate, crate 名称与文件名相同;src/bin/mybin.rs: 名为 mybin 的二进制;src/bin/mybin2/main.rs, src/bin/mybin2/xx.rs: 带 moudle 的,名为 mybin2 二进制,入口为mybin2/main.rs文件;
在有 main.rs 和 bin/xx.rs 的情况下,或者 bin 下面有多个 binary 的情况下,执行 cargo run 时需要通过 --bin 参数来指定 binary name。
main.rs 和 bin/xx.rs 需要通过 package name 引用 lib.rs 及子 module 中的 item:
// src/lib.rs
pub mod mymodule {
pub fn myfunc() {}
}
// src/main.rs
use mycrate::mymodule::myfunc // mycrate 为 Cargo.toml 中定义的 package.name
也可以通过在 Cargo.toml 中通过 [lib] 和 [[bin]] 来配置 root lib 的文件路径(默认 src/lib.rs),和各 bin 的文件路径(默认 src/main.rs)。
[package]
name = "concepts"
version = "0.1.0"
edition = "2021"
[lib] # 一个 package 至多有一个 lib,所以不是数组
name = "foo" # The name of the target. 默认是 package name
path = "src/lib.rs" # The source file of the target.
[[bin]] # 一个 package 可以有多个 bin
name = "event" # 默认是 package name
path = "src/event.rs" # 默认是 src/main.rs,这里进行重定义。
[[bin]]
name = "fib"
path = "src/fib.rs"
module item #
crate 内部使用 module 来对 item 进行分组,增加可读性和复用性,同时可以控制 item 的可见性。
module 由一系列 item 组成,item 的定义顺序无关(宏定义除外),命名解析机制(name resolution)允许在定义 item 前使用该 item。
- 比如在定义 module 前的位置使用该 module 内的 item。
pub fn test2() {
let _ = test(); // 在定义 test 函数前调用它
}
pub fn test() -> Result<(), ()> {
let _s: Result<(), ()> = Err(());
auth::auth_service(); // 在声明 mod auth; 前使用 auth
Ok(())
}
mod auth;
item 类型:
Syntax:
Item:
OuterAttribute* VisItem | MacroItem
VisItem:
Visibility?
(
Module
| ExternCrate
| UseDeclaration
| Function
| TypeAlias
| Struct
| Enumeration
| Union
| ConstantItem
| StaticItem
| Trait
| Implementation
| ExternBlock
)
MacroItem:
MacroInvocationSemi | MacroRulesDefinition
使用 mod 语句来定义或引用 module, module 可以在单独文件或当前文件中定义, 支持嵌套定义:
必须使用 mod auth; 来引用 auth module,使用 use auth; 会报错:
use auth;
pub fn test() -> Result<(), ()> {
let _s: Result<(), ()> = Err(());
auth::auth_service();
Ok(())
}
/*
error[E0432]: unresolved import `auth`
--> src/lib.rs:1:5
|
1 | use auth;
| ^^^^ no external crate `auth`
*/
使用 mod auth; 后,就不需要在使用 use auth;,否则会导致 auth 重复定义。
mod auth;
use auth;
/*
error[E0255]: the name `auth` is defined multiple times
--> src/lib.rs:3:5
|
1 | mod auth;
| --------- previous definition of the module `auth` here
2 |
3 | use auth;
| ^^^^ `auth` reimported here
*/
pub fn test() -> Result<(), ()> {
let _s: Result<(), ()> = Err(());
auth::auth_service();
Ok(())
}
在单独文件中定义子 module,有两种形式(只能二选一,而不能同时存在):
src/<module_name>.rs: 单文件 modulesrc/<module_name>/mod.rs:目录 module,该目录下必须有一个mod.rs文件,使用mod xx;v来声明该目录下的其它子 module 文件;
fern_sim/
├── Cargo.toml
└── src/
├── main.rs
├── spores.rs
└── plant_structures/
├── mod.rs
├── leaves.rs
├── roots.rs
└── stems.rs
单文件 module 也可以有子 module:src/<module_name>.rs + src/<module_name>/xxx.rs:
src/<module_name>/目录下不能有mod.rs文件;- 在
<module_name>.rs中使用mod xx;来引用该目录下的其它子 module;
fern_sim/
├── Cargo.toml
└── src/
├── main.rs
├── spores.rs
└── plant_structures/
├── mod.rs
├── leaves.rs
├── roots.rs
├── stems/ # 目录中不能有 mod.rs 文件
│ ├── phloem.rs
│ └── xylem.rs
└── stems.rs # 目录 module
# 使用
# in plant_structures/stems.rs
pub mod xylem;
pub mod phloem;
和 lib module 类似,binary 也有单文件 binary 和带子 module 的 binary 两种类型:
src/bin/mybin.rs: 单文件二进制 mybin(没有使用 module);src/bin/mybin2/main.rs,src/bin/mybin2/xx.rs: 带子 moudle 的二进制 mybin2,入口为mybin2/main.rs文件;
item 可见性 #
module 的 item 默认(未指定 pub)是私有的:只能在当前 module 和子 module 中使用,但不能在父 module (递归向上) 和其它 crate 中使用。
可以使用 pub 来设置 item 的额外可见性:
pub fn: 对当前 module 和子 module 开放,也对其它 crate 开放;pub (in path::to::module): 对指定的 module 开放;pub (self): 只对当前 module 开放,等效于不加 pub;pub (super): 只对父 module 开放;pub (crate): 对当前 crate 开放;
mod my_mod {
// 缺省是 private, 只在当前 module 和子 module 中使用(可见、开放)
fn private_function() {
println!("called `my_mod::private_function()`");
}
// 使用 pub 来开放可见性
pub fn function() {
println!("called `my_mod::function()`");
}
pub fn indirect_access() {
print!("called `my_mod::indirect_access()`, that\n> ");
private_function();
}
// module 可以嵌套
pub mod nested {
pub fn function() {
println!("called `my_mod::nested::function()`");
}
#[allow(dead_code)]
fn private_function() {
println!("called `my_mod::nested::private_function()`");
}
// 对 crate 的 my_mod 开放
pub(in crate::my_mod) fn public_function_in_my_mod() {
print!("called `my_mod::nested::public_function_in_my_mod()`, that\n> ");
public_function_in_nested();
}
// 对本 module 和子 module 开放
pub(self) fn public_function_in_nested() {
println!("called `my_mod::nested::public_function_in_nested()`");
}
// 对父 module 开放
pub(super) fn public_function_in_super_mod() {
println!("called `my_mod::nested::public_function_in_super_mod()`");
}
}
pub fn call_public_function_in_my_mod() {
print!("called `my_mod::call_public_function_in_my_mod()`, that\n> ");
nested::public_function_in_my_mod();
print!("> ");
nested::public_function_in_super_mod();
}
pub(crate) fn public_function_in_crate() {
println!("called `my_mod::public_function_in_crate()`");
}
mod private_nested {
#[allow(dead_code)]
pub fn function() {
println!("called `my_mod::private_nested::function()`");
}
#[allow(dead_code)]
pub(crate) fn restricted_function() {
println!("called `my_mod::private_nested::restricted_function()`");
}
}
}
fn main() {
// Modules allow disambiguation between items that have the same name.
function();
my_mod::function();
// Public items, including those inside nested modules, can be accessed from outside the parent module.
my_mod::indirect_access();
my_mod::nested::function();
my_mod::call_public_function_in_my_mod();
// pub(crate) items can be called from anywhere in the same crate
my_mod::public_function_in_crate();
}
虽然父 module 的 item 可以在子 module 中可用,但是子 module 还是需要使用 use 来引入该 item,而不能直接使用 item:
// proteins/mod.rs
pub enum AminoAcid { ... }
pub mod synthesis;
// proteins/synthesis.rs
pub fn synthesize(seq: &[AminoAcid]) // error: can't find type `AminoAcid`
// proteins/synthesis.rs
use super::AminoAcid; // explicitly import from parent
pub fn synthesize(seq: &[AminoAcid]) // ok
#[path]/#[cfg_attr]
#
module 可以通过 #[path] 来指定它的文件路径, module 也可以使用 #[cfg_attr] 来指定在 match 条件的情况下,为 module 添加一些 attr,如 path attr:
#[path = "thread_files"]
mod thread {
// Load the `local_data` module from `thread_files/tls.rs` relative to this source file's directory.
#[path = "tls.rs"]
mod local_data;
}
mod inline {
#[path = "other.rs"]
mod inner;
}
// 在 target_os = "linux" 的情况下,添加 #[path = "linux.rs"] 属性,表示 mod os 的文件是 linux.rs;
#[cfg_attr(target_os = "linux", path = "linux.rs")]
// cfg_attr 可以嵌套
#[cfg_attr(target_os = "linux", cfg_attr(feature = "multithreaded", some_other_attribute))]
#[cfg_attr(windows, path = "windows.rs")]
mod os;
#[cfg_attr(feature = "magic", sparkles, crackles)]
fn bewitched() {}
// 等效于:在 feature = "magic" 的情况下,添加下面两个 attr
#[sparkles]
#[crackles]
fn bewitched() {}
use 语句 #
module 引入了一级 namespace, 其中的 item 需要使用 module_name::item 来访问。
use xx::yy 的 xx 可能是当前子 module(优先级更高),也可能是 crate xx。
item path:为了使用 module 中 item,需要使用 :: 分割的路径,路径有两种形式:
- 绝对路径:从 crate root 开始,使用
use crate::module::item;,crate标识符表示本 crate 的根 module,如 lib.rs 或 main.rs 中的 item; - 相对路径:默认从当前 module 开始,使用
self/super关键字或子 module 的标识符;
use: 将某个标识符和一个 full path 绑定, 后续可以直接使用标识符,从而避免了繁琐的、每次都需要使用 full path 引用 item 的问题:
use xx::yy: xx 是相对于当前 module 的, 可以是子 module 或 item(高优), 如果都不存在,则 xx 是 crate 名称;use self::item: 导入当前 module 的 item;use super::item: 导入父 module 的 item;use crate::module::item: 导入从本 crate root 开始的 module 的 item(绝对路径,crate root 对应 lib.rs 或 main.rs 中的 item);::crate: 表示使用外部 crate,如::image表示 image 是 crate 名称,而不会把 image 作为 module name;use crate::module_1::module_2::*;:引入 module_2 下的所有 item;use super::item as sitem;: 使用 as 指定的标识符 sitem 来引用 super::item;use a::b::{self as ab, c as abc};,use a::b::{self as ab, c, d::{*, e::f}};use super::item as _;: 不使用 super::item, 但只需要链接该 crate package;
use 或 pub 使用 crate 标识符来引用 root crate module 中的 item,如 use crate::xxx 引用 crate root module 下的 xxx item。
crate 和 package 名称 #
crate 名称是标识符, 故不支持短横杠,但 Cargo.toml 中指定的 package 名称可以包含短横杠,在使用 extern crate 或 use 来引用 package 名称对应的 crate 时需要将短横杠替换为下划线:
use crate::deeply::nested::{
my_first_function,
my_second_function,
AndATraitType
};
fn main() {
my_first_function();
}
// 使用 use..as 对绑定重命名
use deeply::nested::function as other_function;
fn function() {
println!("called `function()`");
}
mod deeply {
pub mod nested {
pub fn function() {
println!("called `deeply::nested::function()`");
}
}
}
fn main() {
other_function();
println!("Entering block");
{
use crate::deeply::nested::function;
function();
println!("Leaving block");
}
function();
}
pub use #
pub use: 将 item 在本 module 中重新导出, 这样其它 crate 导入本 module 时也可以使用这些 item。
- 常见的情况:将嵌套很深的 item 通过 pub use 在 crate root module 导出,这样使用者就不需要使用很长、很深的 use path 到导入该 item。
pub use导出的 item 在 module 的 cargo doc 中的Reexports部分展示。
// 使用 pub use 将绑定在当前 create module 重新 export
pub use deeply::nested::function as other_function;
在 Rust 2015 以后版本, 一般不需要使用 extern crate xx 语法, 因为编译器从 Cargo.toml 获得依赖的外部 crate 列表。
extern crate hello_world;
extern crate foo as _ // 表示不使用 foo crate 中的 item,只做链接
但 Rust 自带的 crate, 如 alloc,test,proc_macro 只能使用 extern crate 来声明:
// 声明 alloc crate 是 extern 的
extern crate alloc;
// 使用声明的 extern crate 中的 Item
use alloc::rc::Rc;
no_std #
缺省情况下:
- Rust 标准库被自动导入到 crate root module,可以使用 std 来引用它的 item。
- 自动使用
#[macro_use]来导入 std 库中所有#[macro_export]标识宏,这些宏可以在 crate root module 及其所有子 module 可用。 core crate也被导入到 crate root module。
core crate 是 Rust 标准库的依赖,它自身没有任何其它 upstream 依赖(如不依赖系统库、libc 等)。它提供最小化、平台无关的能力,不支持平台相关的 heap 内存分配、并发和 IO 等能力。
通过在 crate level 添加 #![no_std] 可以避免上面隐式自动导入 std 和它的 macro (后续不会链接 Rust 标准库),而只会导入 core crate 和它的 macro。
- 使用
#![no_std]只是关闭了自动导入 std 和它的 macro,代码还是可以使用extern crate std;来显式导入和链接 Rust 标准库。