Cargo.toml
文件可以列取包名,及其指定版本号。用于编译前取得。
cargo build
的技巧:
--verbose
选项:了解包的协作方式;--crate-type lib
选项:告诉 rustc
不要去找 main()
函数执行,而是生成一个.rlib
文件,其中包含编译后的代码,可供之后的 rustc
命令用作输入。--crate-type bin
选项,编译结果是一个二进制文件。--extern
选项:给出当前包用到的每个库的文件名。--release
选项:产生优化后的代码,运行速度更快,但是编译时间会延长。此时,不会检查整数溢出,且会跳过 debug_assert!()
断言。同时,针对诧异生成的栈追踪信息不可靠。构建分析:
命令行 | Cargo.toml 使用的区块 |
---|---|
cargo build | [profile.dev] |
cargo build --release | [profile.release] |
cargo test | [profile.test] |
如果想要分析程序,获得最全面的数据,需要同时启用优化(--release
选项,用于程序的发布构建)和调试(在开发期间调试构建)符号(symbol),那么必须在 cargo.toml
中添加如下代码:
[profile.release]
debug = true # 在发布构建中启用调试标记
debug
设置控制 rustc
中的 -g
选项cargo build --release
可以得到一个带有调试符号的二进制文件。模块是 Rust 的命名空间,也是函数、类型、常量等构成 Rust 程序或库的容器。
mod mod_name1 {
...
}
mod mod_name2;
包解决项目间代码共享的问题,模块解决项目内代码组织的问题。
模块是特性项(item)的集合,通过关键字 pub
标记公有或私有特性项,任何没有标记为 pub
的特性项都是模块私有的。
mod
声明。mod test;
时,既会检查 test.rs
文件,也会检查是否存在 test/mod.rs
文件。如果两个文件都存在,或者都不存在,会报错。::
操作符:用于访问模块的特性。通过绝对路径来引用。
::std
:以双冒号开头,表示引用的是标准库的顶级模块。::std::mem
:引用的是标准库的子模块。::std::mem::swap
:引用的是该模块中的一个公有函数。use
声明:在整个代码块或者整个模块中,导入一个模块或公有函数。
use std::collections::{HashMap, HashSet};
。use std::io::prelude::*
。proteins/mod.rs
中有如下声明:// proteins/mod.rs
pub enum AminoAcid {...}
pub mod synthesis;
proteins/synthesis.rs
中的代码,不会自动看到类型 AminoAcid
:// proteins/synthesis.rs
pub fn synthesize(seq: &[AminoAcid]) { // 错误:找不到类型AminoAcid
...
}
// 修改为如下:
use super::
super
关键字:代表当前模块的父模块。上述代码可修改为如下所示:
// proteins/synthesis.rs
use super::AminoAcid;
pub fn synthesize(seq: &[AminoAcid]) {
...
}
self
关键字:代表当前模块。
// proteins/mod.rs
use self::synthesis::synthesize;
use self::AminoAcid::*;
子模块可以访问其父模块中的私有特性项,但必须通过名字导入每一项。使用 super::*;
只会导入那些被标记为 pub
的特性项
标准库 std
会自动链接到每个程序中,即包含隐藏的声明 extern crate std;
特别常用的名字如:
Vec
和
Result
都会包含在标准前奏里。
std::prelude::vl
是唯一会自动糊导入的前置模块。其中包含了常用的特型和类型。模块由特性项构成,Rust 的主要特性项如下所示:
函数。
类型。
strust
结构体、enum
枚举和 trait
特型。类型别名:
type
关键字,为现有类型定义一个别名。type Table = HashMap>;
。impl
块:
impl
标记 pub
,只能标记个别的方法。常量:
const
关键字用于定义常量。与 let
的区别,可以标记为 pub
,而且必须写明类型。
pub const ROOM_TEMPERATURE: f64 = 20.0;
static
关键字定义静态特型,等价于常量。
pub static ROOM_TEMPERATURE: f64 = 68.0;
常量的值会编译到代码中使用它的每个地方。
mut
。静态变量是在程序运行前就已经存在,且会持续存在,知道程序退出。
mut
。可修改的静态变量,本质上不是线程安全的,绝对不能在安全代码中使用。模块:
导入:
use
关键字。extern crate
关键字。导入一个外部的库。extern
块:
将程序代码改成一个库的步骤:
src/main.rs
重命名为 src/lib.rs
。src/lib.rs
中,要称为库的公有特型的项添加 pub
关键字。main
函数临时转移到其他地方。cargo build
会从 src
代码目录中查找文件,然后决定如何创建。如果看到了 src/lib.rs
,那么就知道是要构建一个库。src/lib.rs
中的代码构成了库的根模块。使用这个库的其他包,只能访问这个根模块中的公有特性。src/bin
中的.rs
文件作为要构建的额外程序。任何特性项都可用属性来修饰。
属性:是写给编译器看的各种指令和建议的普适语法。
常用的属性技巧:
#[allow]
属性:在编译时,禁用某些警告。
// 在编译时,不会报告关于non_camel_case_types关键字的警告
#[allow(non_camel_case_types)]
pub struct git_revspec {
...
}
#[cfg]
属性:将条件编译作为一个特型:
// 只在针对安卓编译时包含此模块
#[cfg(target_os = "android")]
mod mobile;
常用的#[cfg]
语法
#[cfg(...)] 选项 |
启用场景 |
---|---|
test |
启用测试(当以 cargo test 或 rustc --test 编译时) |
debug_assertions |
启用调试断言(通常用于非优化构建) |
unix |
为 Unix(包括 macOS)编译 |
windows |
为 Windows 编译 |
target_pointer_width = "64" |
针对 64 位平台。另一个可能值是 “32” |
target_arch = "x86_64" |
针对 x86-64 架构,其他的值还有:“x86”、“arm”、“aarch64”、“powerpc”、“powerpc64” 和 “mips” |
target_os = "macos" |
为 macOS 编译。其他的值还有 “windows”、“ios”、“android”、“linux”、“openbasd”、“netbsd”、“dragonfly” 和 “bitrig” |
feature = "robots" |
启用用户定义的名为 “robots” 的特性(当以 cargo build --feature robots 或 rustc --cfg feature='"robots"' 编译时)。特性在 Cargo.toml 的 [feature] 部分声明 |
not(A) |
A 不满足时要提供一个函数的两个不同实现,将其中一个标记为#[cfg(x)] ,另一个标记为#[cfg(not(x))] |
all(A, B) |
A 和 B 都满足时,等价于 && |
any(A, B) |
A 或 B 满足时,等价于 || |
#[inline]
属性:对函数的行内扩展,进行一些微观控制。
#[inline]
。#[inline(always)]
,要求每处调用都将函数进行行内扩展。#[inline(never)]
,要求永远不要行内化。#[cfg]
和#[allow]
,可以添加到整个模块中,并应用于其中所有的特性。
#[test]
和#[inline]
,只能添加到个别特性项。
要将属性添加给整个包,需要在 main.rs
或 lib.rs
文件的顶部、任何特性之前添加,而且要用#!
而不是#
标记。
// lib.rs
#![allow(non_camel_case_types)] // 可以将属性添加给整个特性项,而不是其后的个别特性项
pub struct git_revspec {
...
}
pub struct git_error {
...
}
#![feature]
属性:用于开启 Rust 语言和库的不安全特性。比如一些新增的测试功能。
#[test]
属性:标记一些函数,表示将要进行单元测试。
cargo test
会运行所有#[test]
标记的代码,并生成测试结果。
测试中经常使用的两个断言宏,用于检查不变形(invariant):
assert!(expr)
宏:在 expr
为 true
时成功;否则,会诧异并导致测试失败。assert_eq!(v1, v2)
宏:等价于 assert!(v1 == v2)
,但是如果断言失败,那么错误消息会显示两个值。assert!
和 assert_eq!
。debug_assert!
和 debug_assert_eq!
,编写只在调试构建中检查的断言。标记为#[test]
的函数会被有条件编译。cargo build
或 cargo build --release
会跳过测试代码。
当单元测试项比较多,建议放在一个 tests
模块里,并用#[cfg]
属性将整个模块声明为仅供测试使用。
#[cfg(test)] // 只在测试构建时包含此模块
mod tests {
...
}
Rust 默认通过多个线程运行多个测试。cargo test testname
可以禁用多线程,那么每次只运行一个测试。
tests
目录中的.rs
文件。tests
目录与 src
目录放在一起。cargo test
既可运行单元测试,也运行集成测试。如果只运行特定的文件(如 test/unfurl.rs
)中的集成测试,那么可以执行 cargo test --test unfurl
。cargo doc
命令为库创建 HTML
文档
cargo doc --no-deps --open
--no-deps
选项:使 Cargo 只为当前包生成文档,不考虑它所依赖的包。--open
选项:使 Cargo 生成文档后,就在浏览器中打开。Cargo 把新生成的文档文件保存在 target/doc
目录中。
Cargo 生成的文档基于库中的 pub
特型以及它们对应的文档注释生成。
#[doc]
属性:用来标注文档注释。
///
开头的注释,会被视作#[doc]
属性;
/// test
等价于
#[doc = "test"]
//!
开头的注释,也会被视作#[doc]
属性,可以添加到相应的包含特性,通常是模块或包中。
文档注释中的内容会被按照 Mardown 来解析。
Rust 会将文档注释中的代码块,自动转换为测试。
/// #
可以隐藏某些代码行。
no_run
注解:结合代码块界定符号,可以对特定的代码块禁用测试。
/// ```no_run
/// ...
/// ```
ignore
注解:不希望测试代码被编译。
/// ```ignore
/// ...
/// ```
如果文档注释是其他语言,那么需要使用语言的名字标记。
/// ```c++
/// ...
/// ```
指定版本号:
image = "0.6.1"
指定 Git 仓库地址和修订版本:
image = { git = "https://github.com/test/test.git", rev = "rust666" }
指定包含依赖包源代码的目录:
image = { path = "test/image" }
如果发现某个开源包不太合用,那么可以 Fork 下来,然后修改 Cargo.toml 文件中的一行代码即可。接下来,cargo build 会立即切换到 Fork 复制下来的版本,而不再使用官方版本。
版本兼容性:
0.0
开头的版本比较原始,Cargo 不会认为它与任何其他版本兼容。0.x
开头的版本,会被认为同其他 0.x
的版本兼容。1.0
版本,只有新的主版本才会破坏兼容性。支持使用操作符指定版本:
Cargo.toml 中的写法 | 含义 |
---|---|
image = “=0.10.0” | 只使用 0.10.0 版本 |
image = “>=1.0.5” | 使用 1.0.5 版本或更高的版本 |
image = “>1.0.5 <1.1.9” | 使用大于 1.0.5 但小于 1.1.9 的版本 |
image = “<=2.7.10” | 使用小于等于 2.7.10 的版本 |
另一种指定版本的策略是使用通配符 *
(不常用)。
Cargo.lock
可以避免每次构建都升级一次依赖版本。确保在不同的机器上,得到一致的、可再现的构建。Cargo.lock
文件,记录项目使用的每个包的确切版本号。后续的构建都会参考这个文件,并继续使用相同的文件。Cargo.toml
文件中的版本号cargo update
:升级到与 Cargo.toml
中指定版本兼容的最新版本。Cargo.lock
提交到版本控制系统。任何构建这个项目的人,都可以拿到相同的版本。cargo package
命令会创建一个文件(target/package/XXX.0.1.0.crate
),其中包含库的所有源文件,以及 Cargo.toml。这个文件可以上传到 crates.io,与世界共享。
cargo package --list
可以查看包含什么文件。
可以在 Cargo.toml 文件中添加如下许可信息(可删除敏感信息如 authors 中的邮箱):
[package]
name = "test_code"
version = "0.1.0"
authors = ["You " ]
license = "MIT"
homepage = "https://www.example.com"
repository = "https://gitee.com/test/test_code"
documentation = "http://www.example.com/docs"
description = """
Test Code.
"""
登录到 crates.io,并取得 API 秘钥(需要保密),然后只在可控制的安全计算机上执行:
cargo login ^^&@*&...
最后执行 cargo publish
来发布库到 crates.io 上。
Cargo 会保证每个包都有自己的构建目录:target
,包含一份这个包所依赖的独立构建。
Cargo 工作空间(workspace):共享相同构建目录和 Cargo.lock
文件的一组包。可以节省编译时间和磁盘空间。
创建工作空间的方法:
在存储库的根目录下创建一个 Cargo.toml
文件
在其中添加如下代码:其中 fern_sim
等是包含各自包的子目录的名称。
[workspace]
members = ["fern_sim", "fern_img", "fern_video"]
把各个子目录中的 Cargo.lock
文件和 taerget
目录都删除。
做完这些后,无论在任何包中运行 cargo build
,都会自动在根目录中创建一个共享的构建目录,所有包都共享。
cargo build --all
命令会构建当前工作空间中所有的包。
cargo test
和 cargo doc
也支持 --all
选项。
crates.io
上发布了开源包后,包的文档会自动发布到 docs.rs
。README.md
文件。由第三方插件提供 cargo install readme
。支持 cargo readme --help
来学习这个插件。详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第八章
原文地址