📂Project's Structure in Rust
An overview of how the Rust project is structured
💡 Rust uses a specific folder structure to organize your project's code and resources. By using Cargo, we can generate a new package for Rust application.
Cargo
💡 In Rust, Cargo is both the build system and the package manager.
Package Management:
Dependency Management: Cargo helps you declare your project's dependencies on other Rust crates (reusable libraries) using the
Cargo.toml
file.Downloading Dependencies: When you build your project, Cargo automatically downloads the required crates and their dependencies from the Rust Package Registry (crates.io).
Resolving Conflicts: If dependencies have conflicting versions, Cargo attempts to find a compatible combination.
Build System:
Compiling Code: Cargo takes your Rust source code files (
.rs
) and compiles them into machine-readable code (usually an executable or library).Linking Dependencies: It links your code with the downloaded dependencies to create a complete program or library.
Building Different Targets: Cargo allows building your project for different platforms or configurations by specifying targets.
Project Management:
Creating Projects: You can use
cargo new
to create a new Rust project with a basic directory structure.Running Tests: Cargo can run unit tests written for your project using the
cargo test
command.Building Documentation: Some crates support generating documentation using
cargo doc
.
Package Publishing:
Publishing Crates: If you've developed a reusable library, you can publish it to crates.io using
cargo publish
. This makes your crate available to other developers for use in their projects.
Common Cargo Commands
rustup
A command-line tool for managing Rust versions and associated tools.
rustc
Rust compiler that helps to compile the Rust code to an executable binary file.
cargo new <package_name>
It creates a new Rust project.
cargo run
It compiles the code and then run the resultant executable all in one command.
cargo build
because the default build is debug build, hence, Cargo builds the Rust code and creates an executable file in directory target/debug/package_name.
cargo check
It quickly checks the code to make sure it compiles but doesn’t produce an executable file.
cargo build —release
It’s used for compiling the Rust code with optimizations. This command will create an executable in target/release
.
If you want to benchmarking your code’s running time, be sure to run this command and benchmark with executable in target/release
.
cargo update
It’s used to update crates (dependencies).
Rust Package Structure
Basic Components
Cargo will help us to generate a new package. A package contains one or more crates that provide a set of functionality. A package allows you to build, test, and share crates.
Crate is a tree of modules that produces a library or executable. A library is a piece of code that you can share with other crates, while an executable is a program you can execute.
A crate that produces a library is called a library crate, and a crate that produces a executable is called a binary crate
The package rules in Rust:
A package must have at least 1 crate.
A package has at most 1 library crate.
A package has any number of binary crates as needed.
Generate Rust Package
To create a package in Rust, we’ll use Cargo.
In this example, we’ll create a hello_world
package.
After running the command, Cargo will generate a new package with the following structure
src
: This directory will contain the source code of the packagemain.rs
: This is the entry point for all the code of the Rust project. It’s the starting point where execution begins when you run your Rust application.
target
: This directory is a special location automatically generated by Cargo during the build process. It’s used to store the intermediate and final artifacts produced during the build process, and these artifacts can include compiled machine code (executables or libraries), debug information, and temporary files used by Cargo.Cargo.toml
: This is a configuration file that contains package descriptions and dependencies. It’s similar to thepackage.json
file for the JavaScript project.Everything that follows a header in this file is part of that section that continues until another section starts.
In
[dependencies]
, you tell Cargo which external crates your project depends on and which versions of those crates you require.Cargo understands Semantic Versioning (aka
SemVer
), which is a standard for writing version numbers.
Structure Package with Modules
A module is a way to organize and structure code within a package. It groups related functions, structs, enums, and constants together. It provides you the way to control the organization, scope, and privacy of your Rust package.
There are some benefits of modules:
Improve code readability, maintainability, and prevent naming conflicts.
Can be nested, creating a hierarchical structure for your codebase.
Control scope and privacy
Explicitly defined (using the
mod
keyword)Not mapped to the file system
Flexibility and straightforward conditional complication
For example: We're going to structure a crate which is called my_utils
. The my_utils
crate will contain 2 modules: math_utils
and string_utils
modules.
In the main.rs
, we first need to use the mod
keyword to import math_utils
and string_utils
modules, and then use the use
keyword to bring specific functions from the modules into the current scope.
Cargo Workspaces
💡 A Cargo workspace in Rust is a powerful feature that allows you to manage multiple related Rust packages (crates) under a single directory.
Components of a Cargo Workspace
Root Directory
This is the main directory that contains the Cargo.toml
file for the workspace itself.
Member Packages
These are individual Rust packages (crates) within the workspace directory. Each member package has its own Cargo.toml
file specifying its dependencies and configuration.
Shared Dependencies
All member packages within the workspace share a common dependency lock file (Cargo.lock
) located in the workspace root. This ensures consistent dependencies across all packages.
Shared Target Directory
By default, all member packages compile their artifacts (libraries, executables) into a single target directory (target) within the workspace root. This reduces redundancy and simplifies managing build outputs.
Benefits of Using Cargo Workspaces
Centralized Management: Workspaces provide a central location for managing multiple related projects, making development and maintenance more efficient.
Shared Dependencies: Efficient management of dependencies across all packages in the workspace ensures consistency and avoids version conflicts.
Simplified Testing: You can easily test how changes in one package affect other packages within the workspace by running tests across the entire workspace.
Simplified Commands: Common Cargo commands like
build
,test
, andrun
can be executed for the entire workspace or specific member packages using workspace-specific flags.
Common Cargo Workspace Commands
Create Your First Workspace
Now, let’s explore how to establish a workspace and leverage its benefits for modular development.
Setting Up the Workspace
Begin by creating a new directory for your project, for example, my-utils
. This directory will serve as the root of your workspace.
Navigate to the newly created directory and initialize it as a Cargo workspace using the following command. This command creates a Cargo.toml
file at the root of your workspace, which will manage the overall project configuration.
Within the Cargo.toml
file, add a [workspace]
section and specify the member crates for your project. In this example, we'll create three crates:
my-utils
: This will be the binary crate responsible for the application logic.math_utils
: This library crate will house mathematical utility functions.string_utils
: This library crate will contain string manipulation utilities.
Use Cargo to create each member crate within the workspace:
Implementing Functionality in Member Crates
Now, let's add code to each crate to demonstrate their functionalities:
Consuming Utilities in the Binary Crate
Next, we’re going to modify the Cargo.toml
file of the my-utils
crate to declare dependencies on the math_utils
and string_utils
crates:
By setting path
in the dependencies, Cargo instructs Rust to look for the crates within the same workspace instead of external crates.
Finally, we’re going to update main.rs
in the my-utils
crate to import and utilize the functionalities from the other crates:
Now, running cargo run
from the workspace root directory will execute the my-utils
binary and demonstrate how it leverages the functionalities defined in the math_utils
and string_utils
crates.
Last updated