crate
Rust 程序由 crate(板条箱)组成。每个 crate 都是既完整又内聚的单元,包括单个库或可执行程序的所有源代码,以及任何相关的测试、示例、工具、配置和其他杂项。
版本
在 Cargo.toml [package]
部分中修改 edition
表示自己用的是什么版本的 Rust
配置文件
你可以在 Cargo.toml 文件中放置几个配置设定区段,这些设定会影响 cargo 生成的 rustc 命令行.
可以查看附件 Cargo.toml 的配置设定区段对照表
添加编译 debug
[profile.release]
debug = true # 在release构建中启用debug符号
模块
crate 是关于项目间代码共享的,而模块是关于项目内代码组织的。
例子 1
mod spores {
use cells::;
/// 由成年蕨类植物产生的细胞。作为蕨类植物生命周期的一部分,细胞会随风
/// 传播。一个孢子会长成原叶体(一个完整的独立有机体,最大直径达5毫米),
/// 原叶体产生的受精卵会长成新的蕨类植物(植物的性别很复杂)
pub struct Spore {
...
}
/// 模拟减数分裂产生孢子
pub fn produce_spore(factory: &mut Sporangium) -> Spore {
...
}
/// 提取特定孢子中的基因
pub(crate) fn genes(spore: &Spore) -> Vec<Gene> {
...
}
/// 混合基因以准备减数分裂(细胞分裂间期的一部分)
fn recombine(parent: &mut Cell) {
...
}
...
}
pub
关键字会使某个语法项声明为公共项,这样它就可以从模块外部访问了。pub(crate)
关键字可以在这个 crate 中的任何地方使用,但不会作为外部接口的一部分公开。它不能被其他 crate 使用,也不会出现在这个 crate 的文档中。- 未标记为
pub
的内容都是私有的,只能在定义它的模块及其任意子模块中使用 pub(super)
,让语法项只对其父模块可见。
嵌套模块
mod plant_structures {
pub mod roots {
...
}
pub mod stems {
...
}
pub mod leaves {
...
}
}
你希望嵌套模块中的语法项对其他 crate 可见,请务必将它和它所在的模块标记为公开的。
通过这种方式,我们可以写出一个完整的程序,把大量代码和完整的模块层次结构以我们想要的任何方式关联起来,并放在同一个源文件中。
例子 2 用一个文件写一个项目
car.rs
pub mod car {
pub mod engine {
pub struct Engine {
name: String,
}
impl Engine {
pub fn new(name: String) -> Self {
Engine { name }
}
pub fn on(&self) {
println!("engine {} is run", self.name)
}
}
}
mod gearbox {}
mod wheel {}
mod frame {}
mod start {}
}
main.rs
mod car;
fn main() {
let engine = car::car::engine::Engine::new("EA8888".to_string());
engine.on()
}
//engine EA8888 is run
单独文件中的模块
例子 3
文件结构
src
├── car
│ └── engine.rs
├── car.rs
└── main.rs
car/engine.rs
pub struct Engine {
name: String,
power: u8,
}
impl Engine {
pub fn new(name: String, power: u8) -> Engine {
Engine {
name,
power,
}
}
pub fn start(&self) {
println!("Engine {} started power is {}", self.name, self.power);
}
pub fn stop(&self) {
println!("Engine {} stopped {} ", self.name, self.power);
}
}
car.rs
pub mod engine;
main.rs
mod car;
fn main() {
let benz = car::engine::Engine::new("V8".to_string(), 5);
benz.start();
benz.stop();
}
路径与导入
刚刚的列子 我们使用了 car::engine::Engine::new("V8".to_string(), 5);
很麻烦也很长这时我们就可以采用 use
来简化我们的开发
mod car;
use car::engine::Engine;
fn main() {
let benz = Engine::new("V8".to_string(), 5);
benz.start();
benz.stop();
}
可以一次性导入多个模块
use std::collections::; // 同时导入两个模块
use std::fs::; // 同时导入`std::fs`和`std::fs::File`
use std::io::prelude::*; // 导入所有语法项
设置不同的名称
use std::io::Result as IOResult;
// 这个返回类型只是`std::io::Result<()>`的另一种写法:
fn save_spore(spore: &Spore) -> IOResult<()>
...
假如你想看到父的模块你可以使用 super
来进行引入
// proteins/synthesis.rs
use super::AminoAcid; // 从父模块显式导入
pub fn synthesize(seq: &[AminoAcid]) // 正确
...
super
父模块看到self
自己模块看到crate
当前模块::
被称为绝对路径
标准库预导入
Rust 的行为就好像每个模块(包括根模块)都用以下导入语句开头一样.
use std::prelude::v1::*;
公开导入
这意味着 Leaf 和 Root 是 plant_structures 模块的公共语法项
// 在plant_structures/mod.rs中
...
pub use self::leaves::Leaf;
pub use self::roots::Root;
公开结构体字段
pub struct Fern {
pub roots: RootSet,
pub stems: StemSet
}
静态变量与常量
除了函数、类型和嵌套模块,模块还可以定义常量和静态变量。
关键字 const 用于引入常量,其语法和 let 一样,只是它可以标记为 pub,并且必须写明类型。此外,常量的命名规约是 UPPERCASE_NAMES:
pub const ROOM_TEMPERATURE: f64 = 20.0; // 摄氏度
static 关键字引入了一个静态语法项,跟常量几乎是一回事:
pub static ROOM_TEMPERATURE: f64 = 68.0; // 华氏度
将程序变成库
第一步是将现有的项目分为两部分:一个库 crate,其中包含所有共享代码;一个可执行文件,其中只包含你现在的命令行程序才需要的代码。
Cargo.toml
[package]
name = "rust_tutorials"
version = "0.1.0"
authors = ["You <you@example.com>"]
edition = "2021"
- 将文件 src/main.rs 重命名为 src/lib.rs。
- 将 pub 关键字添加到 src/lib.rs 中的语法项上,这些语法项将成为这个库的公共特性。
- 将 main 函数移动到某个临时文件中。(我们暂时不同管它。)
- cargo build
默认设定下,cargo build 会查看源目录中的文件并根据文件名确定要构建的内容。当它发现存在文件 src/lib.rs
时,就知道要构建一个库。
src/lib.rs 中的代码构成了库的根模块。其他使用这个库的 crate 只能访问这个根模块的公共语法项。
src/bin 目录
让原来的命令行程序 rust_tutorials
再次运行起来也很简单,因为 Cargo
对和库位于同一个 crate
中的小型程序有一些内置支持。
将自己的程序和库放在同一个 crate 中。请将下面这段代码放入名为 src/bin/benz.rs
的文件中:
use rust_tutorials::Engine; //rust_tutorials 就是上方 Cargo.toml 中的 name
fn main() {
let benz = Engine::new("V8".to_string(), 8);
benz.start();
benz.stop();
}
既然 rust_tutorials
现在是一个库,那么我们也就多了一种选择:把这个程序放在它自己的独立项目中,再保存到一个完全独立的目录中,然后在它自己的 Cargo.toml
中将 rust_tutorials
列为依赖项:
benz = { path = "./benz" }
属性
Rust 程序中的任何语法项都可以用属性进行装饰。属性是 Rust 的通用语法,用于向编译器提供各种指令和建议。假设你收到了如下警告:
libgit2.rs: warning: type `git_revspec` should have a camel case name
such as `GitRevspec`, #[warn(non_camel_case_types)] on by default
但是你选择这个名字是有特别原因的,只希望 Rust 对此“闭嘴”。那么通过在此类型上添加 #[allow]
属性就可以禁用这条警告:
#[allow(non_camel_case_types)]
pub struct git_revspec {
...
}
可以查看官方文档中更多的 cfg
https://rustwiki.org/zh-CN/reference/conditional-compilation.html?highlight=cfg_attr
测试与文档
Rust 中内置了一个简单的单元测试框架。测试是标有 #[test] 属性的普通函数:
#[test]
fn math_works() {
let x: i32 = 1;
assert!(x.is_positive());
assert_eq!(x + 1, 2);
}
cargo test
会运行项目中的所有测试
$ cargo test
Compiling math_test v0.1.0 (file:///.../math_test)
Running target/release/math_test-e31ed91ae51ebf22
running 1 test
test math_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
文档
cargo doc --no-deps --open
--open
Cargo 随后在浏览器中打开此文档。
--no-deps
只为本身生成文档,而不会为它依赖的所有 crate 生成文档。
文档会保存在 taget/doc
目录下.
添加了 doc
标记 就可以在文档中看到该内容.
#[doc = "Hello Doc"]
//!Hello Doc
fun abc(){
}
指定依赖
image = "0.6.1"
image = { git = "https://github.com/Piston/image.git", rev = "528f19c" }
image = { path = "vendor/image" }
最终对应结果可以看附件中的 Cargo.toml 版本对照表.
cargo.lock 可以控制软件版本一致性.
附件
Cargo.toml 的配置设定区段
命令行 | 使用 Cargo.toml 区段 |
---|---|
cargo build |
[profile.dev] |
cargo build --release |
[profile.release] |
cargo test |
[profile.test] |
Cargo.toml 版本对照表
Cargo.toml 行 | 含义 |
---|---|
image = "=0.10.0" |
仅使用确切的版本 0.10.0 |
image = ">=1.0.5 |
使用 1.0.5 或更高版本(甚至 2.9,如果其可用的话) |
image = ">1.0.5 <1.1.9" |
使用高于 1.0.5 但低于 1.1.9 的版本 |
image = "<=2.7.10" |
使用 2.7.10 或更早的任何版本 |
评论