Rust Projects
Learn about Rust large projects, source files and code base.

Software engineer instructor, software developer and community leader. Book author. Computer enthusiast and experienced programmer. Born in Romania, living in US.
In computer science, a software project refers to the entire process of developing software, including planning, designing, coding, testing, and deploying a program that meets a specific set of requirements.
Concepts
The project concept is the initial idea or plan for a software project. It involves identifying the problem to be solved, defining goals and objectives, and coming up with a rough idea of what the end product will look like. The project concept is typically represented as a set of requirements, specifications, and design documents, and it serves as the foundation for the project.
Project process
Planning phase: Identify requirements, set deadlines, and create a rough design.
Design phase: Create detailed specifications, design the software architecture, and create a road map.
Coding phase: Write software code based on design specifications.
Testing phase: Test the software to identify and fix any bugs or issues.
Deployment phase: Package and install the software on servers, and make it available to users.
Codebase
A project codebase, also known as a code repository, is a centralized location where the source code of a software project is stored and managed. It is a place where developers can collaborate, work on their code, and share changes with others.
A codebase typically includes the source code, documentation, and any project-related files required to build or run the software. It can also include resources such as images, configuration files, and libraries used by the software.
Rust project
In Rust, a project typically refers to a collection of source files and configuration files that work together to create a single executable or library. A project can be as small as a single file or as large as a complex codebase.
Here's a typical structure of a Rust project:
project-name/
├── Cargo.lock
├── Cargo.toml
├── src/
│ ├── lib.rs
│ └── main.rs
└── target/
Cargo.tomlis the manifest file that describes the project, its dependencies, and other metadata. It also serves as the entry point for Cargo — Rust's package manager and build tool.srcdirectory contains the actual code of the project. By default, it contains two files:main.rs(for executable projects) andlib.rs(for library projects). You can also create subdirectories insidesrcto organize your code into modules.Cargo.lockis a file that Cargo generates and updates automatically each time you build the project. It records the exact version of the dependencies used in the project, ensuring that builds are reproducible across multiple machines.targetdirectory is where Cargo stores the output of the build process, including the compiled artifacts of your project.
In addition to these, Rust projects may also include other files and directories depending on their requirements. For example, if the project includes documentation, it may have a docs/ directory. Rust projects may also have a .gitignore file to ignore certain files or directories from being committed to Git.
Overall, Rust's project structure is designed to be simple yet flexible, allowing you to organize your code in a way that suits the needs of your project
Project initialization
To initialize a new Rust project, you can use the cargo command-line tool that comes included with Rust. Follow the steps below to initialize a new Rust project:
Open a terminal or command prompt and navigate to the directory where you want to create your project.
Run the following command to create a new Rust project skeleton:
cargo new project_nameReplace "project_name" with the name you want to give your project. This command will create a new directory with the specified project name, which contains the skeleton code for a Rust project.
Once the project is initialized,
cdinto the newly created project directory by running the following command:cd project_nameYou can now use
cargoto build and run your project. To build your project, run the following command:cargo buildThis will compile your Rust code and create an executable file in the
target/debugdirectory.To run your project, use the following command:
cargo runThis will compile and execute your Rust code. If your code has any output, it will be displayed in the terminal.
That is it! You have now initialized a new Rust project and can begin building your Rust application. After initialization, you can open a project with a text editor and start messing up with the code. Modify the initial program and make your own.
Source code
In Rust, source code is organized into modules, which in turn can be organized into crates and packages. Here's a brief explanation of each of these concepts:
Source code files: In Rust, source code files have a
.rsextension and contain Rust code.Modules: A module is a collection of related code constructs such as types, functions, and constants. Rust provides a module system that allows you to group related functionality together, making your code more organized and modular. Modules can be nested and files can be used to represent a module.
Crates: A crate is a compilation unit in Rust. It is produced when you compile a Rust source code file or a module. A crate can depend on other crates, and it can be published to the Rust package registry (crates.io).
Packages: A package is one or more crates that are intended to be published together. A package contains a
Cargo.tomlmanifest file that specifies metadata about the package, as well as its dependencies. A package can have one or more binaries and/or libraries.Folders: Folders are used to group related files of a package. Rust uses a specific folder structure to organize the files of a package. By convention, there is a
srcfolder that contains the source code files of the package, and atargetfolder that contains the build artifacts.
Here's the relationship between these concepts:
A package is made up of one or more crates
A crate is made up of one or more modules
A module is defined in one or more source code files
Folders are used to group related source code files of a package or module.
To summarize, in Rust, you can group related code constructs into modules, organize modules into crates, and organize one or more crates into packages. Folders are used to group related source code files of a package or module.
Large projects
When organizing a large project in Rust, there are several conventions and best practices that can help keep your code organized and maintainable. Here are some suggestions:
Use a layered architecture: A layered architecture separates your code into logical layers, such as the presentation layer, business logic layer, and data access layer. This helps keep your code organized and makes it easier to maintain and test.
Use the
srcfolder: Rust conventionally uses a folder namedsrcto store the source code of a package. All the Rust source files for your project should go inside thesrcfolder.Use modules to organize your code: Use modules and sub-modules to organize your code. Modules can have their own private and public functions, types and constants, which can be used from other modules.
Use
lib.rsfor library crates andmain.rsfor binary crates: If you have a library crate, put its public API in thelib.rsfile, which serves as the entry point of the library crate. For binary crates, put the application logic in themain.rsfile.Organize your source files by module: One common convention is to create a directory structure that mirrors the module tree. This helps keep related code in the same location.
Avoid large files and duplication: As much as possible, keep files small and focused on a single responsibility. Also, avoid duplication of code across files or modules. Instead, use shared functions or constants across the codebase, which can be called by different parts of the project.
Avoid global state: Use dependency injection to avoid global state. Global state makes your code more difficult to reason about and test.
Write unit tests and integration tests: Write unit tests to test individual units of code and integration tests to test how different parts of your application work together.
Use a build tool: Rust comes with
Cargo, a package manager and build tool. Use this tool to manage dependencies, run tests and build your project.
By following these conventions and best practices, you can keep your large Rust project organized and maintainable.
Project Example
This is a project structure that illustrates how to organize a Rust project that is a simple catalog of people and courses they can follow from a selection of 10 most popular programming languages. Here's the proposed structure:
├── Cargo.toml
├── README.md
├── catalog-backend
│ ├── Cargo.toml
│ ├── src
│ │ ├── main.rs
│ │ ├── db.rs
│ │ ├── api.rs
│ │ └── models.rs
│ └── sql
│ ├── migrations
│ └── schema.sql
├── catalog-frontend
│ └── ...
└── catalog-common
├── Cargo.toml
├── src
│ └── catalog.rs
└── tests
└── catalog_test.rs
catalog-backend:
This package contains the backend code for the catalog application.
Cargo.tomlfile for the main project should contain the following:
[package]
name = "catalog-backend"
version = "0.1.0"
authors = ["Your Name <yourname@example.com>"]
edition = "2018"
[dependencies]
actix-web = "3.3.2"
sqlx = { version = "0.4.0", features = ["mysql"] }
dotenv = "0.10.0"
main.rscontains the entry point of the application.db.rscontains functions to interact with the database.api.rscontains the API routes and handlers.models.rscontains the database models for the application.sql/migrations/contains the SQL migration scripts.sql/schema.sqlcontains the DDL for database schema.
catalog-frontend:
This package contains the frontend code for the catalog application.
This could be any web frontend framework or plain JavaScript.
You can create your own frontend design or download a pre-designed template.
catalog-common:
This package contains code shared between the backend and frontend packages.
Cargo.tomlfile should contain the following:
[package]
name = "catalog-common"
version = "0.1.0"
authors = ["Your Name <yourname@example.com>"]
edition = "2018"
[dependencies]
serde = { version = "1.0.130", features = ["derive"] }
catalog.rscontains the data types defining the catalog.
Database Design:
Database schema could have the following tables:
person: contains information about each person in the catalog, such as name and email address.course: contains information about each course in the catalog, such as title and description.person_course: contains a join betweenpersonandcourse, indicating which courses each person is following.
Functions and Methods:
db.rs: Contains functions to interact with database tables.list_courses: Return a list of courses in the catalog.list_people: Return a list of people in the catalog.add_person(name: &str, email: &str): Add a new person to the catalog.add_course(course_id: i32, person_id: i32): Add a course to a person's list of following courses.remove_course(course_id: i32, person_id: i32): Remove a course from a person's list of following courses.
api.rs: Contains routes and handlers for the REST API.GET /courses: Handler for thelist_coursesfunction.GET /people: Handler for thelist_peoplefunction.POST /people: Handler for theadd_personfunction.POST /people/{person_id}/courses: Handler for theadd_coursefunction.DELETE /people/{person_id}/courses/{course_id}: Handler for theremove_coursefunction.
Individual functions:
utils.rscould contain individual functions to generate reports or manipulate data.generate_report: Generates a report of all the courses and people listed in the catalog.recommend_course: Recommends a course from the catalog based on a person's previous selections.
This structure contains two packages: catalog-backend and catalog-common and both are their own crates. catalog-backend uses actix-web for a web framework to provide the REST API endpoints to manage the catalog of courses and people, and communicate with a MySQL database using sqlx. catalog-common is a shared library crate that contains common datatypes and utility functions that are used by both the backend and front end of the application.
Overall, this structure adheres to Rust conventions and best practices, modules and sub-modules for organizing code, Cargo.toml file for managing dependencies, individual functions for specific tasks and a layered architecture for better maintainability.
Package manager
Cargo is the official package manager for Rust, and it is the standard tool for building, managing and publishing Rust packages. Cargo provides several commands that ease the process of creating, building, testing and sharing Rust projects.
Here's what you can accomplish with Cargo:
Set up a new Rust project and create the necessary file structure.
Manage project dependencies with features and version requirements.
Build and test your Rust code, with output that's easy to understand.
Manage multiple binary, example, and library targets in the same package with different settings and configurations.
Publish and share your Rust code to the world.
Cargo makes it easy to use third-party components in Rust projects. Rust has a growing ecosystem of third-party libraries and crates, which are available for use in your own projects. These crates can provide functionality such as networking, serialization, database interactions, and more.
Some examples of third-party crates that can be used in large Rust projects are:
serde: A powerful Rust library for serializing and deserializing data formats such as JSON, TOML, and XML.
hyper: A fast, modern HTTP implementation for Rust.
diesel: A safe, extensible ORM and Query Builder for Rust.
tokio: A runtime for writing reliable, asynchronous, and slim applications with Rust.
rand: A Rust library for random number generation.
chrono: A library for dealing with dates and times.
reqwest: An ergonomic HTTP client for Rust.
These crates are just a few examples of the many third-party crates available for use in Rust projects. They can be easily integrated into a Cargo-managed project by specifying them as dependencies in the Cargo.toml file. Cargo will then automatically download, build, and link the crates as needed.
In summary, Cargo is the built-in package manager for Rust, and it simplifies the process of building, testing, sharing, and using Rust code. With Cargo, it's easy to manage third-party dependencies and make use of the growing ecosystem of Rust libraries and crates. This makes it easier to build large, complex Rust projects efficiently and quickly.
Module Examples
To define a module in Rust, we use the mod keyword followed by the name of the module, and then we define the contents of the module using curly braces. Here is an example:
mod prime {
fn is_prime(n: u32) -> bool {
if n <= 1 {
return false;
}
for i in 2..n {
if n % i == 0 {
return false;
}
}
true
}
pub fn generate_primes(n: u32) -> Vec<u32> {
if n == 1 {
return Vec::new();
}
let mut primes = generate_primes(n - 1);
if is_prime(n) {
primes.push(n);
}
primes
}
}
In this example, we define a module named prime that contains two functions. The is_prime function checks whether a given number is prime or not, and the generate_primes function recursively generates all prime numbers up to a given number n and returns them as a vector.
We can use the pub keyword to make the generate_primes function public, which means that it can be accessed from outside the module. The is_prime function is not public, which means that it can only be used within the same module.
We can also define nested modules within a module in Rust, by nesting the mod definitions inside the outer module's curly braces. Here is an example:
mod prime {
mod utils {
fn is_valid_number(n: u32) -> bool {
n > 0
}
}
pub fn generate_primes(n: u32) -> Vec<u32> {
if !utils::is_valid_number(n) {
return Vec::new();
}
// ...
}
}
In this example, we define a nested module named utils within the prime module. The is_valid_number function checks whether a given number is greater than zero or not, and it is only accessible from within the same module.
We can then use the utils::is_valid_number function from within the generate_primes function to ensure that the input n is a valid number.
Using a Module
You combine modules and the main() function in a single file but then the file can grow too large. Is a common practice to split a problem into smaller files. You can create one or more library files for your project. Here is an example where the module is outside of the main program.
src/lib.rs:
pub mod prime {
pub fn is_prime(n: u32) -> bool {
if n <= 1 {
return false;
}
for i in 2..n {
if n % i == 0 {
return false;
}
}
true
}
pub fn generate_primes(n: u32) -> Vec<u32> {
if n == 1 {
return Vec::new();
}
let mut primes = generate_primes(n - 1);
if is_prime(n) {
primes.push(n);
}
primes
}
}
src/main.rs:
use rust_chatbot::prime;
fn main() {
let n = 20;
let primes = prime::generate_primes(n);
println!("All prime numbers up to {} are: {:?}", n, primes);
}
In this example, we define the prime module in the src/lib.rs file, and we make it public using the pub keyword. This allows us to use the module in the src/main.rs file.
In src/main.rs, we import the prime module using the use keyword and call the generate_primes function, just like in the previous example.
When you run cargo run in the project directory, the main() function in src/main.rs will be executed, and you should see the output in the terminal window.
cargo.toml
If you do not use IDE you need to update cargo.toml file manually. This requires you to understand toml language. The example above require the following cargo.toml file:
Sure! Here's an example Cargo.toml file that you can use for the previous example:
[package]
name = "rust_chatbot"
version = "0.1.0"
authors = ["Your Name <youremail@example.com>"]
edition = "2018"
[lib]
name = "prime"
path = "src/lib.rs"
crate-type = ["lib"]
[[bin]]
name = "main"
path = "src/main.rs"
[dependencies]
In this Cargo.toml file, we define our package name, version, author, and edition.
We specify that our library is contained in src/lib.rs, and we give it a name of "prime". The crate-type tells cargo that our library should be compiled as a regular library.
We also specify the main binary of our package inside the [[bin]] table, with its name and location.
Finally, there are no dependencies for this project, so we leave the [dependencies] table empty.
You can save this file in the root of your project directory, and run cargo build or cargo run to compile and run your code.
Conclusion
Overall, Rust is a powerful and flexible language that provides many features that make it well-suited for building large projects. The language's focus on safety and performance, combined with its project structure and many powerful features, makes it an excellent choice for complex software systems.
Disclaim: This article was created with ChatGPT. It does not include details about code syntax. To learn more you can drill down into syntax by asking more questions. If you do, feel free to add comments below with the prompts and answers you have received to explain Rust's syntax.
Thank you for reading. You are invited to my Rust online course which is a work in progress but good enough to learn the fundamentals. I teach what I have learned but I'm not a professor or Rust official. I'm just an Engineer that is trying by himself to learn elite Rust programming.
Visit: https://sagecode.pro/rust






