Rust 实战 (2)|创建一个完整的项目

Rust 设计并创建一个完整的项目

    • 工作空间和项目两层结构
    • 设计一个实际的项目
    • 完成创建并编译
    • Hello world
    • 小结

上一节,我们对 Rust 的开发环境的关键概念做了介绍,本节我们来创建一个完整的项目,理解 Rust 的项目结构,并建立一个 Rust 实战用的 git 仓库。

工作空间和项目两层结构

使用过 Visual C++/Visual Studio/… 等 C/C++ IDE 的开发者应该都有解决方案/子项目 的工程概念。例如 Visual Studio 创建的 C++ 工程,都会有一个 *.sln 的解决方案配置文件,每个子项目有 *.projectx 后缀的配置文件。Visual Studio 等 IDE 普遍使用 xml 作为配置文件格式。

Rust 使用 工作空间(workspace)/项目 的方式来组织项目。Rust 的配置文件格式使用的是 TOML 文件格式,这种文件格式的语法噪音很少,也避免了许多采用 yaml 格式做配置文件带来的复杂性。

TOML 文件格式的官方地址是:https://toml.io 从其官网就可以看到简要介绍:

全称:Tom 的(语义)明显、(配置)最小化的语言。(Tom’s Obvious, Minimal Language)
TOML 旨在成为一个语义明显且易于阅读的最小化配置文件格式。
TOML 被设计成可以无歧义地映射为哈希表。
TOML 应该能很容易地被解析成各种语言中的数据结构。

Rust 的工作空间和子项目,都用 Cargo.toml 做配置文件。

设计一个实际的项目

本节我们就来设计一个实际的工程,并一步步了解 Rust 的工作空间和子项目的配置。

首先,创建一个工程目录,命名为 calculator。后续我们对 Rust 的介绍都会基于该工程的功能和需求一步步展开。

mkdir calculator
cd calculator

其次,我们计划使用 Rust 写一个四则运算计算器。设计上我们希望有三个类库(Lib) 子项目和一个主程序(Bin)子项目

  • objects 子项目定义四则运算表达式的**抽象语法树(AST)相关的对象,以及中间语言(IL)**相关的对象。
  • parser 子项目用来编写一个基于 parser combinator 构建一个组合解析库,解析四则运算表达式,输出抽象语法树
  • emitter 子项目用来从抽象语法树生成中间语言(IL)
  • interpreter 子项目用来解释并执行中间语言。
  • calculator 子项目用来完成组装工作,作为程序的入口。

使用 Visual Studio Code 打开 calculator 文件夹:

code .

在根目录下创建一个 Rust 工作空间配置文件 Cargo.toml 并输入如下内容:

[workspace]

members = [

]

其中,[workspace] 表示这是一个工作空间配置文件。members 表示该工作空间包含的子项目数组,初始化是空的。

现在,打开 Visual Studio Code 的终端,使用 cargo 命令来创建一个个类库子项目

cargo new --lib objects

可以看到,工程根目录下生成了 objects 子项目:

├─objects
    ├─src
    │  └─lib.rs
    ├─.gitignore
    └─Cargo.toml

分别解释几个文件:

  • src/lib.rs 是类库的入口文件
  • .gitignore 定义了 git 忽略文件
  • Cargo.toml 定义了子项目依赖配置文件

子项目的 Cargo.toml 文件和工作空间的不同,内容如下:

[package]
name = "objects"
version = "0.1.0"
authors = ["author "]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

大概定义了文件基本信息和最重要的项目依赖项 [dependencies] ,后续添加依赖库的时候会编辑此处。

完成创建并编译

同样的方式,我们可以把其他几个子项目依次创建出来:

cargo new --lib parser
cargo new --lib emitter
cargo new --lib interpreter
cargo new calculator

其中 calculator 是可执行程序,其入口文件是 src/main.rs 文件,而不是src/lib.rs。需要说明的是,Rust 的子项目可以同时是类库项目和可执行程序,只需要同时提供main.rslib.rs 即可。

创建了多个子项目之后,我们并不能马上运行,执行编译命令立刻报错:

cargo build

输出:

error: manifest path D:\calculator contains no package: The manifest is virtual, and the workspace has no members.

我们需要把刚刚创建的几个子项目的路径配置到工作空间里:

[workspace]

members = [
    "objects",
    "parser",
    "emitter",
    "interpreter",
    "calculator"
]

重新执行下编译命令:

cargo build

输出:

   Compiling objects v0.1.0 (D:\calculator\objects)
   Compiling calculator v0.1.0 (D:\calculator\calculator)
   Compiling emitter v0.1.0 (D:\calculator\emitter)
   Compiling parser v0.1.0 (D:\calculator\parser)
   Compiling interpreter v0.1.0 (D:\calculator\interpreter)
    Finished dev [unoptimized + debuginfo] target(s) in 0.47s   

几个子项目成功编译。可以看到根目录下多了一个输出文件夹target, 该目录下有一个debug 子目录,显然我们可以编译 release 版本:

cargo build --release

此时,target 目录下就分别生成了 debugrelease 两个子目录。这两个目录下包含了类库和可执行程序的调试文件和类库文件:

libobjects.d
libobjects.rlib

calculator.d
calculator.pdb
calculator.exe

实际上,我们可以只编译某个特定的子项目,这在大型项目开发中很实用。操作如下:

cd objects
cargo build
cargo build --release

Hello world

最后,我们可以直接在 debugrelease 目录下执行calculator.exe 文件,或者在根目录/calculator目录下直接用 cargo 命令运行:

cargo run

输出:

Finished dev [unoptimized + debuginfo] target(s) in 0.01s
 Running `target\debug\calculator.exe`
Hello, world!

运行 release 版本:

cargo run --release

输出:

Finished release [optimized] target(s) in 0.01s
 Running `target\release\calculator.exe`
Hello, world!

小结

通过本节,我们先设计了一个包含多个类库子项目和一个可执行程序子项目的完整 Rust 项目,接着我们学习了 Rust 的工作空间和子项目的创建和配置,掌握了 Rust 工作空和子项目的两层配置结构,最后我们编译并运行项目,成功地把工程运行了起来,Hellor World!下一节我们会开始正式进入 Rust 代码的编写。

你可能感兴趣的:(Rust,In,Action,rust,编程语言)