Rust工作空间
随着项目的发展,会发现库越来越大,因此希望将包进一步拆分成多个库,
Cargo
提供了一个名为工作空间(workspaces)的功能。可以帮助管理多个相关的、一起开发的包。
创建工作空间
工作空间是一组共享同一个Cargo.lock
文件和输出目录的包。
将有一个包含一个二进制文件和两个库的工作空间,二进制文件提供主要功能并依赖于两个库,一个库提供add_one
函数,一个库提供add_two
函数。
三个crate
将是同一个工作空间的一部分
-
创建工作空间的新目录开
mkdir add cd add
-
新建
Cargo.toml
文件,文件内容如下:[workspace] members = [ ]
-
创建
adder
二进制crate
cargo new adder
可以运行
cargo build
来构建工作空间,此时的目录层级为:H:\TEMP\ADD │ Cargo.toml │ └─adder │ Cargo.toml │ └─src main.rs
工作空间在顶层有一个目标目录(target directory),编译后的产物将会被放入其中,adder包本身没有自己的目标目录,即使我们在adder目录中运行
cargo build
,编译后的产物仍然会出现在add/target
中,而不是add/adder/target
(没有这个目录)Cargo
在工作空间中这样组织目标目录是因为工作空间中的crate
通常是互相依赖的。如果每个crate
都有自己的目标目录,那么每个crate
都必须重新编译工作空间中的其他crate
以将产物放入自己的目标目录,通过共享一个目标目录,create
可以避免不必要的重建。 -
在工作空间中创建第二个 package
创建另一个成员包,并命名为
add_one
cargo new add_one --lib
在add_one中的
add_one/src/lib.rs
添加一个add_one
函数,添加测试函数pub fn add_one(num: i32) -> i32 { num + 1 } #[cfg(test)] mod tests { use crate::add_one; #[test] fn it_works() { let result = add_one(2); assert_eq!(result, 3); } }
然后需要在
adder/Cargo.toml
中添加一个对add_one
的路径依赖,Cargo
并不默认假定工作空间中的crate
会相互依赖,所以需要明确指定依赖关系。在adder crate中使用add_one函数,修改
adder/src/main.rs
中的代码use add_one; fn main() { let num = 10; println!("Hello, world! {} plus one is {}!", num, add_one::add_one(num)); }
通过在顶层的
add
目录中运行cargo build
来构建工作空间!要从 add 目录运行一进制 crate,可以使用cargo run 时带上-p 参数和包名来指定工作空间中要运行的包
cargo run -p adder
执行结果
Hello, world! 10 plus one is 11!
-
在工作空间中引入外部crate
工作空间在顶层只有一个Cargo.lock文件,而不是在每个crate目录中都有一个Cargo.lock,如果添加一个
rand
包到adder/Cargo.toml
和add_one/Cargo.toml
文件中,Cargo
将会解析这两个文件中的rand
版本并记录在唯一的Cargo.lock
文件中,使工作空间中的所有crate
使用相同的依赖项,意味着这些crate
将始终彼此兼容。将
rand
添加到add_one/Cargo.toml
中的dependencies
部分,以便可以在add_one
中使用rand
,同时在adder/Cargo.toml
中的dependencies
添加rand
,两个地方使用不同的版本,但是最后会发现,Cargo
会确保工作空间中的每个包中的每个crate
使用相同版本的rand
,这样既节省空间 ,又确保工作空间中的crate
彼此兼容,如果工作空间中的crate
指定了不兼容版本的同一依赖项,Cargo
将分别解析每个版本,但仍将尽量解析尽可能少的版本。此时各个文件的改动:
add_one/Cargo.toml
[package] name = "add_one" version = "0.1.0" edition = "2021" [dependencies] rand = "0.8.5"
add_one/src/lib.rs
use rand::Rng; pub fn add_one(num: i32) -> (i32, i32) { let mut rng = rand::thread_rng(); let i = rng.gen_range(0..100); return (num + i, i); } #[cfg(test)] mod tests { use crate::add_one; #[test] fn it_works() { let (result, rand_num) = add_one(2); assert_eq!(result, 2 + rand_num); } }
adder/Cargo.toml
[package] name = "adder" version = "0.1.0" edition = "2021" [dependencies] add_one = { path = "../add_one" } rand = "0.8.4"
adder/src/main.rs
use add_one; use rand::Rng; fn main() { let mut rng = rand::thread_rng(); let num = rng.gen_range(0..10); let (res, rand_num) = add_one::add_one(num); println!("Hello, world! {} add {} is {}!", num, rand_num, res); }
-
在工作空间中添加测试模块
新建
add_two
库cargo new add_two --lib
测试代码已添加,运行
cargo test
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml` warning: virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2021 which implies `resolver = "2"` note: to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest note: to use the edition 2021 resolver, specify `workspace.resolver = "2"` in the workspace root's manifest note: for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions Compiling add_two v0.1.0 (H:\temp\add\add_two) Compiling add_one v0.1.0 (H:\temp\add\add_one) Compiling adder v0.1.0 (H:\temp\add\adder) Finished `test` profile [unoptimized + debuginfo] target(s) in 0.38s Running unittests src\lib.rs (target\debug\deps\add_one-ebd2ec5e939dda0d.exe) running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running unittests src\lib.rs (target\debug\deps\add_two-d92d41d98065439b.exe) running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running unittests src\main.rs (target\debug\deps\adder-00ea61a7c623235d.exe) running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests add_one running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests add_two running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
评论区