Dependency management in Rust with Cargo

Dependency management in Rust with Cargo

Greeting!

Cargo is a good Rust package manager that takes care of the heavy lifting of dependency management, project building, testing, and more.

Cargo

Cargo – is the official package manager and build system for Rust. It was designed to simplify working with code on Rust, providing the same project build, dependency management, and plus testing. Like npm for Node.js or Maven for Java, Cargo plays a similar role for Rust.

Main features:

Cargo automatically loads the required libraries (called “crates” in Rust) and their dependencies. Cargo compiles the code using the correct dependency versions and build options.

Cargo allows you to publish your crates on crates.io.

Let’s install and configure

Cargo ships with Rust, so if Rust is already installed, Cargo is already installed:

rustc --version

If the Rust version is visible, then everything is ok

Let’s set up the working environment:

Let’s create a directory in which we will work (folder). Go to the working directory using the terminal and execute the project initialization command:

[dependencies]
my_library = "^1.2.3"

This is a credit file Cargo.toml and catalog src with file main.rs, where we will write code in the future. Through the console, you can create a directory as follows:

$ cargo new hello_world --bin

Open the file Cargo.toml in a text editor. In this file, we will specify project dependencies and settings.

Cargo.toml file

Section [dependencies] the definition of the main dependencies of the project is used. These dependencies will be installed during project build for all configurations:

[package]
name = "my_project"
version = "0.1.0"
edition = "2018"
[dependencies]
my_library = "1.2.3"

Section [dev-dependencies] used for dependencies that are only needed during development, such as for unit testing. They will not be included in the final drafting of the project:

[dev-dependencies]
test_framework = "0.5.0"

Section [build-dependencies] used for dependencies that are only needed during project build time:

[build-dependencies]
codegen_tool = "0.1.0"

You can define optional dependencies in different configurations:

[dependencies]
my_library = { version = "1.2.3", optional = true }

[target] allows you to define dependencies that are specific to certain architectures or build targets, such as build.rs:

[target.'cfg(target_os = "windows")'.dependencies]
windows_library = "2.0.0"

To compile the project, we execute the command cargo build in the terminal. Cargo will compile the project including all dependencies. If there are errors, it will all be displayed.

Version restrictions

Version restrictions allow us to specify the range of versions we want to use. Examples of restrictions:

  • =: Use only a specific version

  • >=: Use versions equal to or newer than specified.

  • <=:Use versions equal to or older than specified.

  • ~: Use versions that are compatible with the specified version, but do not change the MAJOR version.

  • ^: Use versions that are compatible with the specified version, but do not change the MAJOR and MINOR versions.

  • !=: Exclude certain versions

SemVer is an agreement about the versioning format of libraries and dependencies, which allows you to determine what changes have been made to a new version and what features may be broken for developers using this dependency. SemVer includes three version components: MAJOR, MINOR and PATCH.

MAJOR: incremented when incompatible API changes are made.

MINOR (minor number): increases as new functionality is added, but while maintaining backward compatibility. New features can be added, but existing code must work.

PATCH (patch number): incremented when bug fixes or changes that preserve backward compatibility are made, meaning that the changes made fix bugs but do not change functionality.

Suppose we have a dependency in a file Cargo.toml of the following type:

[dependencies]
my_library = "1.2.3"

MAJOR is 1. This means we are using the major version of this library.

MINOR (minor number) is 2. This indicates minor updates with new functionality but with backward compatibility.

PATCH (patch number) is 3. This is an update of fixes and changes with backward compatibility.

Examples of restrictions:

Use versions from 1.2.3 to 2.0.0 (inclusive):

my_library >= "1.2.3", <= "2.0.0"

Use versions compatible with 1.2.0, but do not change the MAJOR version:

my_library = "~1.2.0"

Use versions compatible with 1.2.0, but do not change MAJOR and MINOR versions:

my_library = "^1.2.0"

After specifying the dependencies and their limitations in Cargo.tomlyou can execute the command cargo update on the command line in the root of the project. Cargo will update the dependencies according to the specified restrictions and download the appropriate versions.


Continuing the topic, I want to remind you about free webinars from market experts about safe and unsafe Rust and how Rust encourages the use of composition.

And more courses from OTUS experts can be found in the full catalog.

Related posts