rust学习十四.2、工作空间(workspace)
和大部分语言一样,cargo也可以创建一个工作空间,以便可以包含多个二进制单元和库单元,从而构建较为复杂的工程。
构建这样一个空间空间主要依赖两个手段:Cargo.toml和单元之间的目录结构
从效果上看,rust的Cargo在工作空间上的管理和maven大体相似,但是还不如maven那么的人性化。
一、如何编写顶级Cargo.toml
我们先看看新版本的工作空间的规定:https://doc.rust-lang.org/cargo/reference/workspaces.html
翻译一下。
工作空间的关键点
- 规定成员允许执行的命令
- 所有成员共享一个Cargo.lock,看图1可以明白
- 所有成员共享一个输出目录,看图1可以明白
- 所有成员可以共享包元数据,这个通过定义workspace.package实现
- patch,replace,profile.*只能在顶级toml(工作空间)toml中定义,成员的无效
虚拟工作空间
如果你的工作空间配置(toml)不包含package部分,那么者就是一个虚拟的工作空间。这种情况下,所有的成员的目录都是空间的子目录。
反之,就不是一个虚拟工作空间,也就是我们如果在顶级包中包含了根包(root package),那么就不是一个虚拟工作空间。
作为一个非虚拟空间,也就意味着可以在工作空间配置中定义package有关的一些配置,具体略。
到底要不要虚拟工作空间,纯看个人喜好。
个人倾向于构建一个虚拟的工作空间,因为这个和maven工程更像。
不能做的事情
如果你定义了一个虚拟的工作空间,那么就不能在工作空间配置文件中包含常规单元包所具有的一些配置,例如package,dependencies之类的。
否则编译通过不过
二、单元Cargo.toml
在一个工作空间中,可以有一个二进制包,若干个库单元包。
如果你执行cargo run,那么cargo会自动找到二进制包并运行。
二进制的配置和单独的一个二进制配置并没有什么区别,还有更多,主要是两点:
- 共享工作空间的包配置(package),这个通过 xxx.workspace=true实现,这个xxx可以是name之外的一些包属性
- 共享依赖,这也是工作空间的一个极其重要的目的,避免引入不同的外部单元包,导致版本冲突。这个通过dependencies中xxx.workspace=true实现
其它还包括受限的命令、编译工具(lint)、外部工具元数据。
库单元配置同二进制单元配置,具体略
三、其它
工作空间所涉及的,不止本文提到的一些内容,完整的请参考 https://doc.rust-lang.org/cargo/reference/workspaces.html
四、例子
本文模仿了有关例子,并结合https://doc.rust-lang.org/cargo/reference/workspaces.html的内容,实现了一个简单的工作空间。
4.1、概述
整个空间包含一个二进制库wsmain,以及两个库单元:teacher,student
整个目录结构如下:
图1_示例工作空间wsexample
4.2、配置
共有四个配置文件,一个是工作空间的,三个是单元包的。重点看三个:
1.wsexample
[workspace] members = [ "student", "teacher", "wsmain", ] resolver = "2" [workspace.package] version = "0.1.0" edition = "2021" [workspace.dependencies] rand = "0.9.0-beta.1"
这个配置为所有的成员限定了包的发行版本和版本,并指定了共享的依赖rand。
2.wsmain
[package] name = "wsmain" version.workspace=true edition.workspace=true [dependencies] student={path="../student"} teacher={path="../teacher"}
这个配置指定了wsmain依赖的两个库包,并且包部分共享了工作的包配置(version,edition)
对工作空间内其它库单元依赖的定义是让我失望的地方,不够优雅,个人建议这样:
member.student=true
teacher.sutdent=true
3.student
[package] name = "student" version.workspace=true edition.workspace=true [dependencies] rand.workspace=true
这个配置指定了student共享了工作空间的包配置,并指定了共享工作空间的依赖rand
4.3、代码
二进制单元main.rs代码
use teacher::*; use student::*; fn main() { let lml=studentinfo{ name:String::from("lml"), age:18, gender:String::from("女"), no:String::from("12101") }; print_student(&lml); lml.learn(); lml.sleep(); let lu=teacherinfo{ name:String::from("lu"), age:46, gender:String::from("男"), position:String::from("教研组长"), }; lu.teach_student(&lml); print_teacher(&lu); }
4.4、运行
在一个虚拟工作空间内,无需指定包名,即可运行,例如
cargo run
如果硬要指定也可以,通过参数-p来指定包名,或者--package,例如:
cargo run -p wsmain
结果同cargo run 一样,具体略。
五、小结
- 运用cargo也可以构建一个工作空间,以便包含一个二进制单元和多个库单元
- 在效果上,cargo的构建过程和效果类似maven,但某些方面不如maven来得优雅
- 本文所提到的知识,已经足以解决工厂常见问题:模块化、共享(配置和依赖)
- 更到的高级特性,包括编译,发布等等需要阅读更多相关资料