Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Practical System Programming for Rust Developers

You're reading from   Practical System Programming for Rust Developers Build fast and secure software for Linux/Unix systems with the help of practical examples

Arrow left icon
Product type Paperback
Published in Dec 2020
Publisher Packt
ISBN-13 9781800560963
Length 388 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Prabhu Eshwarla Prabhu Eshwarla
Author Profile Icon Prabhu Eshwarla
Prabhu Eshwarla
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface 1. Section 1: Getting Started with System Programming in Rust
2. Chapter 1: Tools of the Trade – Rust Toolchains and Project Structures FREE CHAPTER 3. Chapter 2: A Tour of the Rust Programming Language 4. Chapter 3: Introduction to the Rust Standard Library 5. Chapter 4: Managing Environment, Command Line, and Time 6. Section 2: Managing and Controlling System Resources in Rust
7. Chapter 5: Memory Management in Rust 8. Chapter 6: Working with Files and Directories in Rust 9. Chapter 7: Implementing Terminal I/O in Rust 10. Chapter 8: Working with Processes and Signals 11. Chapter 9: Managing Concurrency 12. Section 3: Advanced Topics
13. Chapter 10: Working with Device I/O 14. Chapter 11: Learning Network Programming 15. Chapter 12: Writing Unsafe Rust and FFI 16. Other Books You May Enjoy

Automating dependency management

You learned in the previous section how Cargo can be used to set up the base project directory structure and scaffolding for a new project, and how to build various types of binary and library crates. We will look at the dependency management features of Cargo in this section.

Rust comes with a built-in standard library consisting of language primitives and commonly used functions, but it is small by design (compared to other languages). Most real-world programs in Rust depend on additional external libraries to improve functionality and developer productivity. Any such external code that is used is a dependency for the program. Cargo makes it easy to specify and manage dependencies.

In the Rust ecosystem, crates.io is the central public package registry for discovering and downloading libraries (called packages or crates in Rust). It is similar to npm in the JavaScript world. Cargo uses crates.io as the default package registry.

Dependencies are specified in the [dependencies] section of Cargo.toml. Let's see an example.

Start a new project with this command:

cargo new deps-example && cd deps-example

In Cargo.toml, make the following entry to include an external library:

[dependencies]  
chrono = "0.4.0"

Chrono is a datetime library. This is called a dependency because our deps-example crate depends on this external library for its functionality.

When you run cargo build, cargo looks for a crate on crates.io with this name and version. If found, it downloads this crate along with all of its dependencies, compiles them all, and updates a file called Cargo.lock with the exact versions of packages downloaded. The Cargo.lock file is a generated file and not meant for editing.

Each dependency in Cargo.toml is specified in a new line and takes the format <crate-name> = "<semantic-version-number>". Semantic versioning or Semver has the form X.Y.Z, where X is the major version number, Y is the minor version, and Z is the patch version.

Specifying the location of a dependency

There are many ways to specify the location and version of dependencies in Cargo.toml, some of which are summarized here:

  • Crates.io registry: This is the default option and all that is needed is to specify the package name and version string as we did earlier in this section.
  • Alternative registry: While crates.io is the default registry, Cargo provides the option to use an alternate registry. The registry name has to be configured in the .cargo/config file, and in Cargo.toml, an entry is to be made with the registry name, as shown in the example here:
    [dependencies]     
     cratename = { version = "2.1", registry = "alternate-
         registry-name" }
  • Git repository: A Git repo can be specified as the dependency. Here is how to do it:
    [dependencies]
    chrono = { git = "https://github.com/chronotope/chrono" , 
        branch = "master" }

    Cargo will get the repo at the branch and location specified, and look for its Cargo.toml file in order to fetch its dependencies.

  • Specify a local path: Cargo supports path dependencies, which means the library can be a sub-crate within the main cargo package. While building the main cargo package, the sub-crates that have also been specified as dependencies will be built. But dependencies with only a path dependency cannot be uploaded to the crates.io public registry.
  • Multiple locations: Cargo supports the option to specify both a registry version and either a Git or path location. For local builds, the Git or path version is used, and the registry version will be used when the package is published to crates.io.

Using dependent packages in source code

Once the dependencies are specified in the Cargo.toml file in any of the preceding formats, we can use the external library in the package code as shown in the following example. Add the following code to src/main.rs:

use chrono::Utc;
fn main() {
    println!("Hello, time now is {:?}", Utc::now());
}

The use statement tells the compiler to bring the chrono package Utc module into the scope of this program. We can then access the function now() from the Utc module to print out the current date and time. The use statement is not mandatory. An alternative way to print datetime would be as follows:

fn main() {
    println!("Hello, time now is {:?}", chrono::Utc::now());
}

This would give the same result. But if you have to use functions from the chrono package multiple times in code, it is more convenient to bring chrono and required modules into scope once using the use statement, and it becomes easier to type.

It is also possible to rename the imported package with the as keyword:

use chrono as time;
fn main() {
    println!("Hello, time now is {:?}", time::Utc::now());
}

For more details on managing dependencies, refer to the Cargo docs: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html.

In this section, we have seen how to add dependencies to a package. Any number of dependencies can be added to Cargo.toml and used within the program. Cargo makes the dependency management process quite a pleasant experience.

Let's now look at another useful feature of Cargo – running automated tests.

You have been reading a chapter from
Practical System Programming for Rust Developers
Published in: Dec 2020
Publisher: Packt
ISBN-13: 9781800560963
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime