概述
Rust 是一种预编译静态类型(ahead-of-time compiled)的编程语言,除了有基本的 Rust 编程语言标准之外,Rust 官方还提供了一系列适用于各个平台的开发辅助工具以及编译工具链来帮助我们处理 Rust 代码。
rustup
rustup 是 Rust 官方提供的所有相关开发辅助工具以及编译工具链的安装及管理器,使用 rustup
这个工具就可以自动为我们下载安装官方的 Rust 各工具的预编译发行版,并且可以在不同的发行版之间自由切换。
使用
rustup
是一个命令行工具,对应的可执行程序名为 rustup
,详细的使用方法见下图中的命令行参数说明即可。
-
rustup
本身就是代理的原型,它自身没有代理! -
为了方便管理各种工具,rustup 引入了 component、profile、proxy 等基本概念来组织和管理所有相关工具的发布,详见之前博文 Rust 之一 组件介绍、版本发布、开发环境搭建 中的详细介绍!
配置
rustup
(rustup-init
) 在运行时会自动读取 CARGO_HOME
和 RUSTUP_HOME
这两个环境变量的值,其中,CARGO_HOME
指定了 Cargo 相关工具以及缓存的存放位置, 而 RUSTUP_HOME
则指定了编译工具链的位置。
-
RUSTUP_DIST_SERVER
(之前叫RUSTUP_DIST_ROOT
,现已弃用 )指定了编译工具链的下载的地址,默认是 https://static.rust-lang.org -
RUSTUP_UPDATE_ROOT
指定了rustup
自更新的地址,默认值是 https://static.rust-lang.org/rustup -
其他配置项见 https://rust-lang.github.io/rustup/environment-variables.html
源码
rustup 的源码仓库为 https://github.com/rust-lang/rustup,可以直接 git clone https://github.com/rust-lang/rustup.git
获取。rustup 是 multirust 的继承者。Rustup 最初是 multiust-rs,由 Diggory Blake 将 multirust 从 shell 脚本改写为 Rust,现在由 Rust 项目维护。
-
rustup
源码中实现了根据名字的不同而实现不一样的功能,.cargo/bin
中的各个工具以及我们安装使用的rustup-init
实际上都是它改成了不同名字。 详见src\bin\rustup-init.rs
中函数main()
➜run_rustup()
➜run_rustup_inner()
的内容
-
对于类 Unix 系统,官方推荐的
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
安装方法实际上就是下载并执行源码中的rustup-init.sh
这个脚本 -
源码下的
doc
文件夹中是文档的源码,分别对应 The rustup book 和 The Rustup developer guide。文档使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍
-
源码下的
www
文件夹中是 https://rustup.rs/ 网站的源码,这个网站貌似很少使用。-
直接在
www
文件夹执行python -m http.server 8000
可以本地部署 -
这个不是 https://rust-lang.org/ 的源码,https://rust-lang.org/ 的源码是独立仓库 https://github.com/rust-lang/www.rust-lang.org
-
构建
rustup 的源码本身就是一个标准的 Rust 项目工程(在 Rust 之三 项目管理(Package、Crate、Workspace 等)、文档规范、编码规范 中详细介绍),因此,我们可以使用 cargo build
命令一键编译出名为 rustup-init
可执行程序,这个是 rustup-init
实际上就是 rustup
,详见下面的说明。
- 如果在 Windows 下构建,则必须要安装 Visual Studio Build Tools 2017 or later(必选 Workloads select Visual C++ build tools 以及 Individual componenets 中的 C++/CLI support、C++ CMake tools for Windows)、NASM、CMake、ninja
cargo
Cargo 是 Rust 的包管理器,可以用于依赖包的下载、编译、更新、分发等操作。同时, Cargo 还是一个命令行项目管理工具,它有一套自己的项目结构(在 Rust 之三 项目管理(Package、Crate、Workspace 等)、文档规范、编码规范 中详细介绍),可以帮助我们自动调用编译工具链来编译 Rust 项目源码。
使用
cargo
是一个命令行工具,对应的可执行程序名为 cargo
,详细的使用方法见下图中的命令行参数说明即可。
-
当我们调用
cargo
命令时实际上是通过.cargo/bin/cargo
这个代理来调用.rustup/toolchains/发行版/bin/cargo
这个真正的可执行程序。
-
cargo 默认的工作目录为
$HOME/.cargo
,其中的目录说明如下:
bin
:包含了通过cargo install
(只有拥有二进制目标文件的包能够被安装,并会被安装到$HOME/.cargo/bin
中) 或rustup
下载的包编译出的可执行文件。可以(默认)将该目录加入到$PATH
环境变量中,以实现对这些可执行文件的直接访问registry
:这个是 Cargo 下周的各种 Crate 的缓存目录,包含了注册中心(例如 crates.io)的元数据和 Packagesindex
: 包含了注册中心中所有可用包的元数据( 版本、依赖等 )cache
: 保存了已下载的依赖,这些依赖包以 gzip 的压缩档案形式保存,后缀名为.crate
src
: 若一个已下载的.crate
被一个 package 所需要,该档案会被解压缩到此目录下,最终 rustc 可以在其中找到所需的 .rs 文件
-
Cargo 管理的项目的基本单位被称为
Package
。一个 Package 中至少包含一个 Crate;一个 Package 中最多只能包含一个库 Library Crate;一个 Package 中可以包含任意多个 Binary Crate,详见之后的博文 Rust 之三 项目管理(Package、Crate、Workspace 等)、文档规范、编码规范。 -
cargo 内置的子命令只有
build
、check
、doc
、run
、test
、remove
等几个,当输入其他子命令时,它支持自动查找cargo-xxx
的其他可执行文件,详见后文源码章节的说明! -
绝大多数情况下,我们都是使用
cargo
命令来编译项目,而不是直接使用rustc
配置
cargo 在工作时会自动读取 Windows 平台下的 %USERPROFILE%\.cargo\config.toml
或者类 Unix 系统下的 $HOME/.cargo/config.toml
或者当前项目目录下的 .cargo/config.toml
中的配置信息(在 cargo 1.38 及以前的配置版本使用文件名 config
)。
-
当前项目目录下的
.cargo/config.toml
优先级高于系统用户目录下的配置文件! -
config.toml
中的内容最常用的就是更改软件包的镜像源地址。 -
在 crates.io 之外添加新的注册服务,配置如下:
[registries] ustc = { index = "https://mirrors.ustc.edu.cn/crates.io-index/" }
对于这种方式,我们的项目的 Cargo.toml 中的依赖包引入方式也得对应的改
[dependencies] time = { registry = "ustc" }
-
直接使用新注册服务来替代默认的 crates.io,配置如下
[source.crates-io] replace-with = 'ustc' [source.ustc] registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"
示例:
c[source.crates-io] registry = "https://github.com/rust-lang/crates.io-index" replace-with = "ustc" # 或 rustcc、sjtu、tuna # 根据 replace-with 配置的镜像源,以下只需配置一个对应的源即可 # 中国科大 [source.ustc] registry = "git://mirrors.ustc.edu.cn/crates.io-index" # rustcc 社区 [source.rustcc] registry = "https://code.aliyun.com/rustcc/crates.io-index.git" # 上海交通大学 [source.sjtu] registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index" # 清华大学 [source.tuna] registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
-
CARGO_TARGET_DIR
定义了构建输出目录,默认是target
-
CARGO_HOME
这个环境变量定义了 cargo 默认所在的目录,其中包含了 Cargo 可执行程序、配置文件、缓存文件等等,我们通过修改这个环境变量就可以重定位 Cargo 的位置。-
Windows 平台下默认值是
%USERPROFILE%\.cargo
;Unix 类系统下默认值是$HOME/.cargo
-
当我们使用 Cargo 构建项目时,下载的各种 crates 就位于
.cargo/registry
目录中
-
cargo-binutils
cargo-binutils 是由 Rust 官方的嵌入式工作组(Rust on Embedded Devices Working Group)中的工具组(Embedded WG Tools team)维护的一个非官方项目,这个项目可以为我们的 cargo
命令增加一些子命令,使用这些子命令可以直接调用指定编译工具链对应的 LLVM 的相关工具。
安装
-
cargo install cargo-binutils
-
rustup component add llvm-tools
示例
当安装了 cargo-binutils 和 llvm-tools 之后,我们就可以直接使用如下命令了(cargo xxx
就是通过直接查找 cargo-xxx
可执行程序来实现的):
cargo objcopy
用于将 ELF 转换为 BINcargo nm
输出符号表cargo objdump
反汇编
cargo build
cargo build
会编译我们的项目代码,结果会被放入项目根目录下的 target
文件夹中,target 目录的结构取决于是否使用 --target
标志为特定的平台构建。target
的位置可以通过设置 CARGO_TARGET_DIR
环境变量、build.target-dir
配置项以及 --target-dir
命令行参数这三种方式来更改。
-
cargo build --verbose
可以看到调用rustc
的详细参数 -
编译库时,Cargo 会自动使用
rustc
的--crate-type lib
选项,进而生成.rlib
的库文件;编译可执行程序时,Cargo 会自动使用rustc
的--crate-type bin
,进而生成可执行文件 -
对于每个 Crate 的编译,Cargo 都会为
rustc
指定--extern
选项,给出当前 crate 将使用的每个库的文件名,Rust 会将代码静态链接到最终的可执行文件中
不使用 --target
若不使用 --target
标志,则 Cargo 会根据宿主机架构进行构建,构建结果会放入项目根目录下的 target 目录中,target 下每个子目录中包含了相应的发布配置 profile 的构建结果,例如 release、debug 是自带的 profile,前者往往用于生产环境,因为会做大量的性能优化,而后者则用于开发环境,此时的编译效率和报错信息是最好的。
目录 | 描述 |
---|---|
target/debug/ | 包含了 dev profile 的构建输出(cargo build 或 cargo build --debug) |
target/release/ | release profile 的构建输出,cargo build --release |
target/foo/ | 自定义 foo profile 的构建输出,cargo build --profile=foo |
除此之外我们还可以定义自己想要的 profile ,例如用于测试环境的 profile: test,用于预发环境的 profile :pre-prod 等。出于历史原因:
- dev 和 test profile 的构建结果都存放在 debug 目录下
- release 和 bench profile 则存放在 release 目录下
- 用户定义的 profile 存在同名的目录下
使用 --target
当使用 --target XXX
为特定的平台编译后,输出会放在 target/XXX/
目录下:
target 子目录说明
在 profile 文件夹中(例如 debug 或 release)包含编译后的最终成果:
目录 | 描述 |
---|---|
target/debug/ | 包含编译后的输出,例如二进制可执行文件、库对象( library target ) |
target/debug/examples/ | 包含示例对象( example target ) |
还有一些命令会在 target 下生成自己的独立目录:
目录 | 描述 |
---|---|
target/doc/ | 包含通过 cargo doc 生成的文档 |
target/package/ | 包含 cargo package 或 cargo publish 生成的输出 |
Cargo 还会创建几个用于构建过程的其它类型目录,它们的目录结构只应该被 Cargo 自身使用,因此可能会在未来发生变化:
目录 | 描述 |
---|---|
target/debug/deps | 依赖和其它输出成果 |
target/debug/incremental | rustc 增量编译的输出,该缓存可以用于提升后续的编译速度 |
target/debug/build/ | 构建脚本的输出 |
源码
cargo 的源码仓库为 https://github.com/rust-lang/cargo,直接使用 git clone https://github.com/rust-lang/cargo.git
获取。
- cargo 会自动查找
.cargo/bin/
中以cargo-
开头的其他可执行文件,并把他们作为子命令来使用!
- 源码的
/src/doc
目录下是文档 The Cargo Book 的源码,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍
构建
cargo 的源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build
命令一键编译出名为 cargo
可执行程序。
Clippy
Clippy 是Rust 官方开发的一个基于 LLVM 的 Rust 代码检测及优化工具(类似于 PC-lint 这类工具),它可以自动检测 Rust 代码中的潜在问题,并提供相应的建议。Clippy 使用 Lint 规则来检查代码,这些规则基于 Rust 的编译器插件系统。
cargo-clippy
是一个命令行工具,对应的可执程序为 cargo-clippy
。当我们使用时实际上是通过 .cargo/bin/cargo-clippy
这个代理来调用 .rustup/toolchains/发行版/bin/cargo-clippy
这个真正的可执行程序。
cargo clippy
通常,cargo-clippy
是作为 cargo
命令的一个子命令 cargo clippy
来间接使用
clippy-driver
Clippy 可以抛开 Cargo 来独立使用,此时需要使用 clippy-driver
命令,其参数则需要与 rustc
的参数相同,例如 clippy-driver --edition 2018 -Cpanic=abort foo.rs
。
配置
默认情况下,cargo-clippy
会自动查找名为 clippy.toml
或 .clippy.toml
的文件,搜索的位置优先级由高到低为 CLIPPY_CONF_DIR
、CARGO_MANIFEST_DIR
、当前项目目录,并执行其中的规则。
-
除了配置文件中存放的部分 Lint 规则外,还可以直接在
cargo-clippy
的命令行中指定一些规则。 -
目前,Clippy 中包含了超过 700 条 lints,这些 lints 被划分为了多个类别,每个类别都对应一个默认的 lint level。在
rustc
中,Lint 被分为了 allow、warn、force-warn、deny、forbid 这 5 level。
-
Cargo.toml
中的[lints.clippy]
中也可以指定一些规则 -
在源代码中也可以使用特定的属性,例如
#![allow(clippy::all)]
来指定规则
源码
Clippy 的源码仓库为 https://github.com/rust-lang/rust-clippy,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build
命令一键编译出名为 cargo-clippy
和 clippy-driver
的可执行程序。
文档
-
官方文档 Clippy Documentation
-
文档的源码位于 Clippy 源码的 book 目录下,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍
Rustfmt
Rustfmt 是 Rust 官方提供的格式化 Rust 代码的工具(类似于 LLVM 的 clang-format
这类工具),它根据一些预定义的规则(括号的使用、缩进的级别、空格的添加等)来自动将 Rust 代码格式化为统一的风格。
rustfmt
是一个命令行工具,对应的可执程序为 rustfmt
,使用命令 rustfmt [options] <file>...
来对指定的源码文件来进行格式化。当我们使用时实际上是通过 .cargo/bin/rustfmt
这个代理来调用 .rustup/toolchains/发行版/bin/rustfmt
这个真正的可执行程序。
cargo fmt
通常 rustfmt 会被作为 cargo
命令的一个子命令 cargo fmt
来间接使用,完整格式是 cargo fmt [OPTIONS] [-- <rustfmt_options>...]
,其中的 [-- <rustfmt_options>...]
会直接传递给 rustfmt
来进行使用,因此它的取值就是 rustfmt 的选项。
cargo-fmt
此外,rustfmt 默认还会提供一个名为 cargo-fmt
的可执行程序,这个工具的作用于 cargo fmt
类似,不过它不依赖于 Cargo 这个工具进而实现类似的功能,因此,在不用 Cargo 时,我们可以直接使用 cargo-fmt
来一键格式化整个项目的源文件。
配置
在默认情况下,rustfmt
会自动读取当前项目各级目录下的 rustfmt.toml
或 .rustfmt.toml
文件,并根据其中的规则来进行格式化配置文件所在的目录的代码。如果子目录下没有配置文件,但是它的父级目录有,则根据父级目录的配置文件格式化。
- 如果项目目录下不存在
rustfmt.toml
或.rustfmt.toml
,则会尝试读取用户目录 - 如果用户目录下也没有,则读取 rustfmt 全局配置目录下的全局配置文件
- 配置文件中的各种规则直接查看源码中的
Configurations.md
或者使用在线 https://rust-lang.github.io/rustfmt/ 版本,两者是一样的!
源码
Rustfmt 的源码仓库为 https://github.com/rust-lang/rustfmt,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build
命令一键编译出名为 cargo-fmt
和 rustfmt
的可执行程序。
文档
目前,Rustfmt 的文档就是源码里的 Configurations.md
,源码目录的 docs
中的 index.thml
中就是通过 JavaScript 调用的 Configurations.md
的内容!
rust-analyzer
rust-analyzer 是 Rust 编程语言的语言服务器协议的实现。它为许多代码编辑器(VS Code、 Emacs 和 Vim)提供了代码完成、语法高亮和转到定义等特性。它也是一个命令行工具,不过,我们通常不会直接使用它,而是由代码编辑器插件来帮我们调用。
-
由于 rust-analyzer 通常不会由我们直接使用,所以,默认安装 Rust 后没有 rust-analyzer
-
实际上就是一个模块化编译器前端或者说一个动态分析库,通过 LSP(Language Server Protocol)与 VS Code、 Emacs 和 Vim 等通信
-
rust-analyzer 依赖 Rust 标准库的源码,通常需要手动执行
rustup component add rust-src
来安装,安装后的位置为.rustup\toolchains\toolchain_name\lib\rustlib\src\rust
-
rust-analyzer 提供的功能都是通过调用其他 Rust 工具来实现的,例如,代码格式就会调用
rustfmt
源码
rust-analyzer 的源码仓库为 https://github.com/rust-lang/rust-analyzer,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build
命令一键编译出名为 rust-analyzer
和 rust-analyzer-proc-macro-srv
的可执行程序。
文档
官方在线文档 。
- 官方在线文档
- 文档的源码位于 rust-analyzer 源码的 docs 目录下,,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍
配置
配置项众多,参见 https://rust-analyzer.github.io/manual.html#configuration 即可。
mdBook
mdBook 是一个使用 Markdown 创建出在线文档网站的命令行工具。它是创建产品或 API 文档、教程、课程材料或任何需要简洁、易于导航和可定制的演示文稿的理想工具。Rust 提供的绝大多数文档都是使用 mdBook 来构建的!
安装
默认安装 Rust 后比不会安装 mdbook
,我们需要使用 cargo install mdbook
手动进行安装,mdbook
可执行文件会被放到 .cargo/bin
目录中。也可以直接在 https://github.com/rust-lang/mdBook/releases 上下载预编译好的可执行文件来使用!或者直接从源码构建出可执行程序!
目录结构
使用 mdbook init xxx
命令就会创建出名为 xxx 的文档目录结构,自动生成如下所示的目录结构
-
book.toml
文件按是配置文件 -
新增的 Markdown文章都要放到 src 目录下,可以自行创建子目录来进行分类存放
-
src/SUMMARY.md
是所有文章的目录,新增的 Markdown文章都需要在这里面写一下 -
book
文件夹用于存放 mdBook 构建后产生的 HTML 的文档
发布文档
在文档目录中,执行 mdbook build
就可以一键生成 HTML 格式的静态网页文档,默认存到当前目录的 book
文件夹中。在编写时,可以使用 mdbook serve
命令可以启动本地 Web 服务器,我们可以直接在浏览器中以 localhost 进行预览
源码
mdBook 的源码仓库为 https://github.com/rust-lang/mdBook,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build
命令一键编译出名为 mdbook
的可执行程序。
文档
- 官方在线文档
- 文档的源码位于 mdBook 源码的 guide 目录下,本身也是使用的 mdBook 文档系统。首先安装
cargo install mbook
或者自己构建后,在doc\guide
中执行mbook build
或者在mbook build 指定路径
就可以构建出上面的官方在线文档
rustdoc
rustdoc 是 Rust 官方提供的随附编译工具链发布的文档处理工具,这个工具主要是用来自动收集当前项目源码文件中的各种注释来生成对应文档 ,它接受一个 crate root 文件或者一个 Markdown 文件作为参数,然后生成 HTML,CSS 和 JavaScript 文件等称为一套以静态网页为主的文档。
rustdoc 是一个命令行工具,对应的可执程序为 rustdoc
,使用命令 rustfmt [options] <input>
自动收集源码中的注释形成文档。当我们使用时实际上是通过 .cargo/bin/rustdoc
这个代理来调用 .rustup/toolchains/发行版/bin/rustdoc
这个真正的可执行程序。
- rustdoc 以 crate root 文件为入口自动遍历所以使用到的源码文件,不用我们手动指定所有源码文件。
- 默认的文档名字是 Crate 的名字,可以使用
--crate-name xxx
来进行更改 - 文档将存放到
target\doc
目录下,每个 Crate 一个子目录,他们之间会互相引用
cargo doc
通常 rustdoc
会被作为 cargo 命令的一个子命令 cargo doc
来间接使用,完整格式是 cargo doc [OPTIONS]
。这个命令需要在我们的 Package 目录下执行,Cargo 将自动查找所有源代码文件(包括我们依赖的 Crate 的源码文件)。
-
使用
cargo doc --verbose
可以看到调用rustdoc
的详细参数
-
cargo doc
目前不支持独立的 Markdown 文件 -
cargo doc
命令不止处理我们自己的源码文件,还会处理依赖的 Crate 的源码文件,进而形成一整套的文档。运行cargo doc --open
命令来构建所有本地依赖项提供的文档,并在浏览器中打开,这样就方便了我们查找需要的接口
源码
rustdoc 稍微有点特殊,它是作为 Rust 的编译工具链中的一个标准组件来发布的。因此,它没有一个单独的源码仓库,而是在 Rust 编译工具链的仓库 https://github.com/rust-lang/rust 中的一个部分
文档
-
官方文档 The rustdoc book
-
文档的源码位于 Rust 源码的
src\doc\rustdoc
目录下,可以使用命令x.py doc src/doc/rustdoc
来构建,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍
文档注释
rustdoc 会提取源码文件中特定格式的注释(官方称为文档注释)来生成文档,rustdoc 规定了用于生成的文档的注释必须以 ///
或者 //!
开头,并且注释的内容主要使用 Markdown 语法来进行编写。
///
用于放在函数、结构体、变量等代码前面以对代码进行注释,称为 outer documentation,示例如下:
//!
用于放到一个源码文件的开头给一个源码文件注释,称为 inner documentation。示例如下:
- 当它位于 root crate 文件时,那么它就是文档主页的注释
- 其最后面需要加一个空行
- 可以在自己的
src/lib.rs
或main.rs
的开头添加#![warn(missing_docs)]
,而如果是一个要共享的库则可添加#![deny(missing_docs)]
- 使用
#[doc(hidden)]
可以屏蔽将之后的内容提取到文档中