2

Sylvain Kerkour - How to deal with large Cargo workspaces in Rust

 2 years ago
source link: https://kerkour.com/blog/rust-large-cargo-workspace/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to deal with large Cargo workspaces in Rust

Tue, Aug 17, 2021

I’m a big fan of monoliths, but when Rust projects become larger and larger, we have to use Cargo workspaces. First to benefit from incremental compilation to speed up compile times, second to improve code organization.

Then a big problem arises: the declaration of our dependencies is scattered all over the place, with the same dependencies declared in dozens of different Cargo.toml files. Updating a single dependency used by all our packages (uuid for example) now requires us to crawl through all these Cargo.toml files and manually update the dependencies one by one. This problem is exacerbated by the tendency of Rust’s ecosystem to have a lot of 0.x packages.

So here is a little trick I’ve found to eases large projects maintenance.

The thing is to use a single libs crate that will declare all our dependencies, re-export these dependencies in lib.rs, and then import this libs crate in all the other crates of the workspaces.

$ ls
app
Cargo.lock
Cargo.toml
libs
one
target
three
two

libs/Cargo.toml

[package]
name = "libs"
version = "0.1.0"
edition = "2018"

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

[dependencies]
tokio = { version = "1", features = ["full"] }
uuid = "0.8"

libs/src/libs.rs

pub use tokio;
pub use uuid;

one/Cargo.toml

[package]
name = "one"
version = "0.1.0"
edition = "2018"

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

[dependencies]
libs = { path = "../libs" }

one/src/libs.rs

use std::time::Duration;
use libs::tokio::time;

pub async fn sleep_one() {
    time::sleep(Duration::from_secs(1)).await;
}

app/Cargo.toml

[package]
name = "app"
version = "0.1.0"
edition = "2018"

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

[dependencies]
libs = { path = "../libs" }

one = { path = "../one" }
two = { path = "../two" }
three = { path = "../three" }

app/src/main.rs

use libs::tokio;

#[tokio::main]
async fn main() {
    one::sleep_one().await;
    two::sleep_two().await;
    three::sleep_three().await;
}

Limitations

Be aware that this technique may not work with all crates: some crates providing macros requires to be directly declared in the dependencies list. For example serde or sqlx.

As usual, you can find the code on GitHub: github.com/skerkour/kerkour.com

Join the private club where I share exclusive tips and stories about programming, hacking and entrepreneurship. 1 message / week.
I hate spam even more than you do. I'll never share your email, and you can unsubscribe at any time.
black_hat_rust_cover.svg

Want to learn Rust and offensive security? Take a look at my book Black Hat Rust. All early-access supporters get a special discount and awesome bonuses: https://academy.kerkour.com/black-hat-rust?coupon=BLOG.
Warning: this offer is limited in time!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK