02-工程化构建工具cargo

192 阅读8分钟

工程化构建工具与包管理器 Cargo.png

前言

在上一篇文章《前端工程师的 Rust 学习之旅——hello world》中,我们已经了解了 Rust 的一些特点和应用场景,也已经安装好了 Rust 的基础运行环境,并带大家一起实现了最简单的 helloworld 程序。但我们也提到了,rustc 这个编译工具只适合一些简单的程序,在我们实际的工程项目中,rustc 的能力就远远不够了。此时,我们就需要清楚今天的主角:cargo 粉末登场了。

Cargo 简介

cargoRust 的构建系统和包管理工具。前端同学应该都知道,NodeJs 的包管理工具是 npm,而 cargo 也承担了包管理的重任。比起 npm 纯粹的只是进行依赖包管理,cargo 还兼具了项目工程化构建的任务,功能更加强大。

总结下来,cargo 能够帮我们做这些事情:

  • 构建代码
  • 下载依赖库
  • 构建依赖库
  • CLI管理工具,如创建新的 Rust 项目

或许有同学会问,cargo 需要我们额外安装吗?答案是不需要,在我们安装 Rust 的时候,就已经自动地安装上了 cargo 了。也就是,只要你把 Rust 环境搭建好了,你就可以直接使用 cargo 进行工程化开发了,就这么简单。

我们可以使用一下命令查看 cargo 是否已经正确安装了:

cargo --version

681705223603_.pic.jpg

当你看到类似这样的输出时,就说明你电脑上已经安装好了 cargo 了。

常用命令

new - 创建项目

我们可以使用 cargo 创建一个全新的 Rust 项目,我们只需要运行如下命令:

cargo new rust_domo1_cargo

运行输出如下信息就说明已经创建成功了:

691705223603_.pic.jpg

我们进入到创建好的 rust_demo1_cargo 目录中看一下:

701705223603_.pic.jpg

细心的同学或许也发现了,我们这个在创建项目的时候,默认个给这个项目初始化了一个 git 本地仓库,并添加了 .gitignore 文件。这是我们直接新建项目的默认操作,如果你的项目中使用的版本管理工具不是 git,或者是,你完全不需要版本管理工具,可以使用如下命令进行创建:

# 不需要版本管理工具
cargo new rust_demo1_cargo --vcs none
# 使用 hg 作为版本管理工具
cargo new rust_demo1_cargo --vcs hg
# 目前 cargo 支持的版本管理工具有:git, hg, pijul, fossil, none,默认不指定则使用 git

我们可以看到,cargo 帮我们生成了一个 Cargo.toml 文件,以及在 src 目录下创建了一个 main.rs,那么,Cargo.toml 是用来干嘛的呢?

Carogo.toml

toml:Tom's Obvious, Minimal Language

我们把这个文件打开看看:

711705223603_.pic.jpg

很多前端同学应该反应就会反应过来,这不就是 “package.json”吗?

没错,其实 Cargo.toml 文件在 Rust 当中的作用就相当于是 package.json 在前端项目当中的作用,主要用来描述当前项目的一些关键信息,如包名、版本号、依赖包等等。

在这个文件当中,使用[]包裹的是一个区域的标题,表示从这一行开始,直到下一个遇到 [] 为止,这中间的内容都是用来配置这个区域的内容的。

[package]

说明下面的内容都是用来配置 package 即包信息内容的。

常见的属性有:

  • name:包名
  • version:版本号
  • authors:包作者信息,是一个字符串数组,可以填写多个,如:authors = ["kiner-tang <1127031143@qq.com>"]
  • edition:使用的 Rust 的版本,如上图说明我现在正在使用的 Rust 的版本是 2021

[dependencies]

这部分是项目依赖项的区域,会将我们当前项目需要的依赖包的信息列举在这个标签下面,而我们的项目在构建运行的时候,cargo 也会根据这个依赖项列表帮我们去下载依赖项。跟 NodeJs 当中的 dependencies 的作用是一样的。跟 NodeJS 很类似的,在 Rust 中也有 dev-dependencies 用于管理开发依赖,这也跟 NodeJs 的开发依赖不谋而合。

721705223603_.pic.jpg

731705223603_.pic.jpg

build - 构建项目

了解了如何创建项目,并知道了大概得项目结构和文件的作用之后,我们再来看看应该如何构建项目。

我们会使用以下命令进行项目的构建:

# 在项目的根目录执行
cargo build

如果看到以下信息就说明我们已经构建成功了:

741705223603_.pic.jpg

我们可以看到,构建成功之后,会在根目录下面生成一个 target 目录,这里面就是我们编译生成的文件。

上图中 target/debug/rust_domo1_cargo (如果在 windows 系统中,应该是生成同名的带有 .exe 后缀的文件)文件就是我们编译生成的一个可执行文件,就像上一篇文章《前端工程师的 Rust 学习之旅——hello world》中使用 rustc 生成的文件一样。我们也可以直接运行这个文件:

751705223603_.pic.jpg

Cargo.lock

细心的同学应该发现了,当我们运行 build 命令之后,在我们项目的根目录新增了一个 Cargo.lock 的文件,这个文件有什么作用呢?

这个文件负责追踪项目依赖的精确版本。这个跟我们 NodeJs 项目中的 package-lock.jsonyarn.lock 文件的作用类似。

这个文件不需要我们手动修改,每次 build 的时候 cargo 会根据当前项目的依赖使用情况对这个文件进行更新。

761705223626_.pic.jpg

run - 构建并运行

上面的构建操作后,我们还需要自己找到目标执行文件进行执行。实际上,cargo 给我们提供了一个更加便捷的命令用于构建并执行这个项目。

cargo run

运行上述命令后,cargo 首先会对项目进行构建,构建成功之后,直接执行可执行文件。当然,如果我们之前已经执行过 run 命令产生了可执行文件,并且我们没有对源码进行修改的话,那么执行 run 命令时将会跳过编译构建的过程,直接执行这个文件。(这个有点类似于缓存的概念,如果文件 hash 没有改变,就没有必要从获取服务器获取文件信息,而是直接拿本地缓存)。

771705223626_.pic.jpg

check - 代码检查

这个命令有点像我们 NodeJs 当中的 lint 操作,对我们代码的语法进行检查,但不会生成任何的可执行文件。我们尝试把上面的代码修改一下:

fn main() {
  // 把文件最后的分号去掉
  println!("hello world!")
  // 新增一行
  println!("hi")
}

然后执行以下命令:

cargo check

781705223626_.pic.jpg

我们可以看到,运行 cargo check 命令后,cargo 会帮我们检查出来程序当中的语法错误。相较于其他语言仅仅知识展示错误而言,Rust 更是贴心的给出了我们修改的意见,例如上图中,提示我们应该在箭头的位置加上 ; 以解决这个报错。这样看来,Rust 的开发调试体验明显是比其他语言跟好的。很多人用过 Rust 之后就爱不释手也有这一方面的原因。

但是,或许有些同学会有疑问,我在 build 或者是 run 的时候,如果有报错也会提示出来呀,为啥还要多此一举地增加这个命令呢?我们来想一下,假如说我们这个项目已经迭代到一定的量级了,项目文件成百上千,如果每次都通过 build 或者是 run 来检查语法错误的话,效率无疑是非常低的,而我们的 check 因为不需要生成任何的可执行二进制文件,它的执行效率是远高于另外两个命令的。因此,在大型项目当中,check 语法检查的高效就能派上用场了。此外,如果我们的项目是在 githubgitlab 这些平台上托管的,我们在提交代码之后,通常需要用 ci 进行自动化的检测,以确保提交上来的代码至少不会有语法错误,我们也可以使用这个命令。

build --release - 为发布构建

我们上面运行 build 仅仅只是为了用于执行或调试,如果我们的项目已经开发好了,准备发布了,那么此时我们可以执行以下命令构建正式文件:

cargo build --release

加上 --release 之后,相较于直接运行 build 命令有什么区别呢?

其实这就有点像前端项目中 NODE_ENV=developmentNODE_ENV=production 模式的区别,一个是开发模式的构建,一个是生产模式的构建。

主要有以下几个区别:

  • 代码运行效率提升:在编译时会对代码进行优化,可以让可执行文件的执行效率更高(就像是在前端项目当中,我们执行生产模式构建时,会对代码进行压缩、移除不必要的 console.log、对没有使用到的代码进行摇树等等优化一样)。
  • 编译时间增加:编译时需要对代码进行优化,因此相较于普通构建时间更长(同样的,既然多了一个对代码优化的时间,编译的总时长自然也就会相应增加,这跟前端项目中的生产构建命令也是类似的)。
  • 生成目录不一样:生成的可执行文件的目录是在:target/release 下面,而不加 --release 则是生成在 target/debug 下面。

791705223626_.pic.jpg

结语

至此,关于 cargo 的一些介绍以及常见的用法就介绍完了,当然,cargo 的功能其实远远不止这些,我们可以在开发过程中,运行 cargo -h 查询更多的用法,或者是在官方文档中获得更多用法的介绍。