Rust Toolchain
| Toolchain/version manager | Rust compiler | Code formatter | Linter | Package manager | Package registry |
|---|---|---|---|---|---|
rustup | rustc | rustfmt | clippy | cargo | crates.io |
I. Introduction 💡
-
rustup: the Rust toolchain installer and updater. This tool is used to install and updaterustcandcargowhen new versions of Rust are released. In addition,rustupcan also download documentation for the standard library. You can have multiple versions of Rust installed at once andrustupwill let you switch between them as needed. -
rustc: the Rust compiler which turns.rsfiles into binaries and other intermediate formats. -
rustfmt: a tool for formatting Rust code according to style guidelines. It ensures that Rust code is consistent and adheres to community standards. -
clippy: a collection of lints to catch common mistakes and improve your Rust code. It provides suggestions for idiomatic Rust usage and helps maintain code quality. -
cargo: the Rust dependency manager and build tool. Cargo knows how to download dependencies, usually hosted on https://crates.io, and it will pass them torustcwhen building your project. Cargo also comes with a built-in test runner which is used to execute unit tests. -
crates.io: the official package registry for Rust, where developers can publish, discover, and manage Rust libraries (crates). It serves as the primary source for Rust packages and dependencies.
II. Editions 📚
Editions in Rust are a structured way to introduce new features and improvements while maintaining backward compatibility. This ensures that code written in older editions will still compile with newer compilers.
Rust editions are released every three years, allowing significant changes to be introduced without breaking existing code.
So far, Rust has had three editions: 2015, 2018, and 2021. Each edition introduces new syntax, features, and improvements.
The edition is specified in the Cargo.toml file under the [package] section, guiding the Rust compiler on which edition's rules to apply.
Code from different editions can coexist within the same project, allowing for gradual migration to newer editions.
Tools like cargo fix assist in migrating code to a newer edition by automatically applying necessary changes.
Editions enable Rust to evolve and improve without disrupting existing projects, making it easier for developers to adopt new features at their own pace.
III. Crate 📦
Rust implements code modularity and organization through its crate system.
crates, the fundamental compilation units in Rust, can be compiled into executable binaries or libraries. This structure promotes code modularity, reusability and separation of concerns.
The crate system also facilitates code sharing, enabling developers to reuse functional modules across different projects, thereby enhancing development efficiency and reducing duplication of effort. Through crate, Rust provides robust support for structuring large projects and collaborative development.
Binary Crate 💻
Usually main.rs is used as the entry point, which is compiled to produce an executable.
// src/main.rs
fn main() {
println!("Hello, world!");
}
Library Crate 📦
Usually lib.rs is the main file, which is compiled to generate libraries for use by other programs.
// src/lib.rs
pub fn greet(name: &str) -> String {
println!("Hello, {}!", name);
}
This crate can be referenced by other crate.
// src/main.rs
use my_library::greet;
fn main() {
let message = greet("Alice");
println!("{}", message);
}
IV. Modules 📚
Modules in Rust are similar to namespaces in C++ and packages in Java.
In Rust, a single crate can contain multiple modules.
When we talk about functional modules, we're referring to grouping functions or structures based on their functionality. It's common to organize similar functions, or functions and structures that implement the same functionality or work together to implement a functionality, into a single module.
1. Define modules 📦
mod module_name {
fn function_name() {
// ……
}
}
In Rust, modules are private by default. If a module or a function within a module needs to be exported for external use, the pub keyword must be added. Private modules cannot be called by other external modules or programs. All functions in a private module must be private, while a public module can have both public and private functions.
pub mod public_module {
pub fn public_function() {
// ……
}
fn private_function() {
// ……
}
}
mod private_module {
fn private_function() {
// ……
}
}
2. Call module 📞
use pub_module::function_name;
Create library:
cargo new –lib mylib
// src/lib.rs
/// Adds two numbers and returns the result.
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
/// Multiplies two numbers and returns the result.
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
Go to the mylib directory and execute cargo build.
Open the root directory Cargo.toml
[dependencies]
mylib = { path = "..//mylib" }
// src/main.rs
use mylib::{add, multiply};
fn main() {
let sum = add(5, 10);
let product = multiply(5, 10);
println!("Sum: {}", sum);
println!("Product: {}", product);
}
Rust allows nesting of one module within another, or, in other words, multiple levels of modules. To call or use a nested module use two colons (::) to splice modules from left to right, from outside to inside.
use mod1::mod2::mod3::method_name;
fn main() {
method_name();
}
3. Path 🛤
Absolute path: start at crate root, use crate name or the literal value crate;
Relative path: start from the current module, use self, super or the identifier of the current module;
If the defined part and the used part always move together, use a relative path; if they can be detached independently, use an absolute path.
my_crate
├── src
│ ├── lib.rs
│ └── utils.rs
// src/utils.rs
pub fn greet() {
println!("Hello!");
}
// src/lib.rs
pub mod utils;
fn main() {
my_crate::utils::greet();
crate::utils::greet();
}
my_crate
├── src
│ ├── lib.rs
│ └── module_a.rs
│ └── module_b.rs
// src/module_a.rs
pub fn function_a() {
println!("Function A");
self::function_b();
}
pub fn function_b() {
println!("Function B");
}