cargo 项目也称为 package, 一个 package 包含一个或多个 crate, crate 分为 3 种类型: binary、library、 procedure_macro。
使用 cargo new my-project
命令创建 package,一个 package 必须包含一个 library 或 binary crate,至多包含一个 libraray crate, 可以包含任意数量的 binary crate(src/bin/ 目录下每个文集对应一个 binary
crate)。
crate 是 Rust 的编译、发布、版本化、加载的单元, crate 由一系列可以嵌套的 module 组成,其中 crate root module 是 Rust 编译器开始编译的文件:
- src/main.rs: binary crate 的 crate root,crate 名称与 package 名称相同;
- src/lib.rs: library crate 的 crate root(一个 package 包含 0-1 个 library crate),crate 名称与 package 名相同;
module 由一系列 item 组成,item 的定义顺序无关(宏定义除外),命名解析机制(name resolution)允许在定义 item 前后来使用该 item。
item 类型:
Syntax:
Item:
OuterAttribute* VisItem | MacroItem
VisItem:
Visibility?
(
Module
| ExternCrate
| UseDeclaration
| Function
| TypeAlias
| Struct
| Enumeration
| Union
| ConstantItem
| StaticItem
| Trait
| Implementation
| ExternBlock
)
MacroItem:
MacroInvocationSemi | MacroRulesDefinition
use 或 pub 使用 crate
标识符来引用 root crate module 中的 item,如 use crate::xxx 引用 crate root
下的 xxx item;
# 创建 bin crate
cargo new foo
# 创建 lib crate
cargo new --lib bar
.
├── bar
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── foo
├── Cargo.toml
└── src
└── main.rs
# bin 目录下的各文件为独立的 binary, 可以通过 cargo --bin my_other_bin 来指定
使用 mod 来引用其它 module 文件中的 item, 该 mod 可以是单独的文件或当前文件中定义, mod 可以嵌套。
- src/<module_name>.rs // module 单文件
- src/<module_name>/mod.rs // module 目录,该目录下必须有一个 mod.rs 文件。
父 module 的 item, 不管是否 public, 都可以在自身和子 module 中使用,但是不能在父 module (递归向上) 和其它非子 module 中使用。
在 Rust 2015 以后版本, 基本上不需要再使用 extern crate xx
了, 因为编译器从 Cargo.toml 获得依赖的外部 crate 列表。但是 Rust 自带的 crate 如 alloc/test/proc_macro
只能 extern crate 来声明:
extern crate alloc;
use alloc::rc::Rc;
crate 名称是标识符, 不支持短横杠,但 Cargo package 名称可以包含短横杠,所以 Cargo.toml 中指定的
package name 可以包含短横杠,但在使用 extern crate
或 use
来引用 package 中的 crate 时,需要将短横杠替换为下划线:
extern crate hello_world;
extern crate foo as _ // 表示不使用 foo crate 中的 item,只做链接
module 引入了一级 namespace, 其中的 item 需要使用 module::item 来访问.
- module 是在一个 crate 内部,用于将代码进行分组,增加可读性,易于复用;
- 控制项目(item)的可见性。
- module 可以嵌套,可以包含其它 item,如 struct/enum/常量/trait/函数等定义等;
module 中 item 的可见性:
- pub fn: 只对当前 module 和子 module 开放;
- pub (in path::to::module): pub(in crate::my_mod), 对指定的 crate 开放;
- pub (self):: 只对当前 module 开放; 等效于不加 pub;
- pub (super):: 只对父 module 开放;
- pub(crate):: 对当前 crate 开放;
mod my_mod {
// 缺省是 private
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()`");
}
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();
}
pub(self) fn public_function_in_nested() {
println!("called `my_mod::nested::public_function_in_nested()`");
}
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 可以通过 #[path] attribute
来指定它的文件路径, 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(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() {}
item path:为了使用 Rust 模块中的 item,需要使用 :: 风格的路径,路径有两种形式:
- 绝对路径:从 crate root 开始,使用
use crate::module::item;
- crate 表示本 crate 的根 module,如 lib.rs 或 bin.rs 中的 item;
- 相对路径:从当前 module 开始,使用 self/super 或当前 module 的标识符;
use: 将某个标识符和一个 full path 绑定, 后续可以直接使用标识符:
- use xx::yy 中的 xx 是相对于当前 module 的, 可以是子 module 或 item, 如果都不存在则 xx 是 crate 名称;
- use self::item 导入当前 module 的 item;
- yse super::item 导入父 module 的 item;
- use crate::module::item 导入从 crate root 开始的 module 的 item(绝对路径)
- 开头表示使用外部 crate image, 而不会把 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 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 将绑定在当前 create module 重新 export
pub use deeply::nested::function as other_function;
pub use
将 item 在本 moudule 中重新导出, 这样其他 crate 导入该 module 时也可以使用这些 item. 常见的情况是将嵌套很深的 item 通过 pub use 在 crate root module 导出,这样使用者就不需要使用很长、很深的 use path 到导入该 item.
pub use 导出的 item 在 module 的 cargo doc 中的 “Reexports”
部分展示;
缺省情况下, Rust 标准库被自动导入到 crate root module,可以使用 std 来引用它中的 item。同时隐式的为 std 使用 macro_use attribute 来导入 std 库中 macro_export 的所有宏。同时 core 也被导入到 crate root module。
通过在 crate level 添加 #![no_std]
attr 可以避免上面隐式自动导入 std 和它的 macro,而只会导入 core crate
和它的 macro。
- 使用
#![no_std] attr
只是关闭了自动导入 std 和它的 macro,代码还是可以使用 extern crate std; 来显式导入和链接 Rust 标准库。
注:可以使用 https://github.com/dtolnay/cargo-expand 来展开 macro 代码:
- 也可以使用
cargo build --verbose
来看到展开的代码:
// cat src/main.rs
#[derive(Debug)]
struct S;
fn main() {
println!("{:?}", S);
}
// 展开: cargo expand
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*; // 自动导入了 std::prelude
#[macro_use]
extern crate std; // 自动导入了 std crate 和其中 export 的 macro
struct S;
#[automatically_derived]
#[allow(unused_qualifications)]
impl ::core::fmt::Debug for S {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match *self {
S => {
let mut debug_trait_builder = f.debug_tuple("S");
debug_trait_builder.finish()
}
}
}
}
fn main() {
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["", "\n"],
&match (&S,) {
(arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
},
));
};
}