Rust π¦ programming language learning.
Mindmap:
For Linux or macOS
Including VMs
Following tools get installed: rustup
, rustc
, cargo
, rustfmt
Different release channels
- stable: stable, but has a 6-week stabilization period
- beta: unstable, but has a 6-week stabilization period
- nightly: unstable, but has the latest features
rustup
is for managing different rust toolchain versions for different targets/architectures (arm, x86, etc.)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
info: downloading installer
Welcome to Rust!
This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.
Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:
/Users/abhi3700/.rustup
This can be modified with the RUSTUP_HOME environment variable.
The Cargo home directory is located at:
/Users/abhi3700/.cargo
This can be modified with the CARGO_HOME environment variable.
The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:
/Users/abhi3700/.cargo/bin
This path will then be added to your PATH environment variable by
modifying the profile files located at:
/Users/abhi3700/.profile
/Users/abhi3700/.bash_profile
/Users/abhi3700/.bashrc
/Users/abhi3700/.zshenv
You can uninstall at any time with rustup self uninstall and
these changes will be reverted.
Current installation options:
default host triple: aarch64-apple-darwin
default toolchain: stable (default)
profile: default
modify PATH variable: yes
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
Use VSCode.
Extensions:
-
"[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer", "editor.formatOnSave": true },
-
Error Lens [OPTIONAL]
The list might get updated over the time. Refer to your VSCode extension list.
An ideal setup for a rust code (with tests) would be:
So, here on left terminal, we have $ cargo watch -x run
running, which will watch for changes in the project and automatically run. On right terminal, we have $ cargo watch -x test
running, which will watch for changes in the project and automatically test.
-
set
stable
as default toolchain via$ rustup default stable
-
set
nightly
as default toolchain via$ rustup default nightly
-
set
nightly
as default toolchain for a specific project via$ rustup override set nightly
If any repository has
rust-toolchain.toml
file present, then the toolchain version mentioned in the file will be used for that project if you have already switched to that toolchain. -
All the rust binaries are installed in this folder
$HOME/.cargo/bin
-
Update all channels using
$ rustup update
[RECOMMENDED]stable-aarch64-apple-darwin updated - rustc 1.72.1 (d5c2e9c34 2023-09-13) (from rustc 1.72.0 (5680fa18f 2023-08-23)) beta-aarch64-apple-darwin updated - rustc 1.74.0-beta.1 (b5c050feb 2023-10-03) (from rustc 1.73.0-beta.3 (bc28abf92 2023-08-27)) nightly-aarch64-apple-darwin updated - rustc 1.75.0-nightly (187b8131d 2023-10-03) (from rustc 1.74.0-nightly (84a9f4c6e 2023-08-29))
- And then need to set the latest
rustc
&cargo
for individual channels:stable
,beta
,nightly
$ rustup default stable-aarch64-apple-darwin
$ rustup default beta-aarch64-apple-darwin
$ rustup default nightly-aarch64-apple-darwin
- And then need to set the latest
-
Update the toolchain (of stable channel):
$ rustup update stable
-
Update all toolchains (irrespective of channel):
$ rustup update
-
View installed version via
$ rustup show
-
Check if there is any latest version using
$ rustup check
-
install a specific version via
$ rustup install 1.64.0
or$ rustup install 1.64.0-aarch64-apple-darwin
-
set a specific version in the already set channel via
$ rustup default 1.64.0
or$ rustup override set 1.64.0
-
Uninstall rustup using
$ rustup self uninstall
-
toolchains:
- List all installed toolchains using
$ rustup toolchain list
- Install a toolchain using
$ rustup toolchain install <toolchain-name-w-version-arch-developer>
. E.g.$ rustup toolchain install 1.70.0-aarch64-apple-darwin
- Uninstall a toolchain using
$ rustup toolchain uninstall <toolchain-name-w-version-arch-developer>
. E.g.$ rustup toolchain uninstall 1.70.0-aarch64-apple-darwin
- List all installed toolchains using
-
lib:
- Show all available lib using
$ rustup component list
- Show all installed lib using
$ rustup component list --installed
- Install rust std lib using
$ rustup component add rust-src
- Show all available lib using
-
target (for a toolchain):
- Show all available target using
$ rustup target list
- show all installed target using
$ rustup target list --installed
- Install rust target using
$ rustup target add <component-name>
. E.g.$ rustup target add wasm32-unknown-unknown
Here,
unknown
means that it is for any OS.
- Show all available target using
-
Some components to be installed
- clippy:
$ rustup component add clippy
- rustfmt:
$ rustup component add rustfmt
- rust-src:
$ rustup component add rust-src
- docs:
$ rustup component add rust-docs
- clippy:
$ cargo new <package-folder-name>
: Create a new project with folder & package named as given.$ cargo new hello
: Create a project folder named 'hello' & the package name ashello
inCargo.toml
file.
$ cargo new <folder-name> --name <package-name>
: Create a new project with folder name different than the package name.$ cargo new demo --name game-demo
: Here,demo
is the folder name whereas the package name isgame-demo
inCargo.toml
file.
$ cargo add <package-name>
: add package to[dependencies]
locally into the rust project. E.g.$ cargo add dotenv
.$ cargo add <package-name> --dev
: add package to[dev-dependencies]
locally into the rust project. E.g.$ cargo add hex-literal --dev
.- Add dependency via git:
token_contract = {git="https://github.com/AztecProtocol/aztec-packages/", tag="aztec-packages-v0.51.1", directory="noir-projects/noir-contracts/contracts/token_contract"}
$ cargo upgrade
: update your dependencies inCargo.toml
file to the latest version.cargo-edit
must be installed viacargo install cargo-edit
.- View the dependency graph of a project using
$ cargo tree
-
$ cargo install --list
: list globally installed packages via . -
$ cargo install --path <path/to/folder/containing/Cargo.toml>
: install the binaries in by default/target/release
(by default) folder . This will install the binary in the~/.cargo/bin
folder. In case of workspace, parse the path of the bin folder containingCargo.toml
file.- After this, if you want to uninstall the binary, then you can do it via
$ cargo uninstall <bin-name>
. Suppose, you installed a binary namedhello
via$ cargo install --path <path/to/folder/containing/Cargo.toml>
, then you can uninstall it via$ cargo uninstall hello
.
- After this, if you want to uninstall the binary, then you can do it via
-
Install a git repo using
cargo
:$ cargo install --git https://github.com/dimensionhq/fleet fleet-rs
. And then use fleet like this:fleet -h
-
Install from git repo:
cargo install --git https://github.com/dioxuslabs/dioxus.git dioxus-cli # or cargo install --git https://github.com/dioxuslabs/dioxus.git --rev <commit-hash> dioxus-cli
-
Install
cargo-edit
: for helping with edit, add, remove, upgrade, downgrade, and list dependencies inCargo.toml
-
Install
cargo-watch
: via$ cargo install cargo-watch
.- Watch for changes in the project and automatically run via
$ cargo watch -x run
- Watch for changes in the project and automatically test via
$ cargo watch -x test
- Watch for changes in the project and automatically run via
-
cargo-expand
: install via$ cargo install cargo-expand
. more here -
cargo-audit
: install via$ cargo install cargo-audit
.
cargo doc --open
: Open doc of project. Used when it's not a workspace.cargo doc --package <PACKAGE_NAME> --open
: Open doc of package in a workspace
$ cargo update
: This command will update dependencies in the Cargo.lock file to the latest version. If the Cargo.lock file does not exist, it will be created with the latest available versions.
-
$ cargo +nightly build
: build usingnightly
toolchain for a project -
$ cargo build
: build a debug version of a project using set toolchain (view via$ rustup show
). -
$ cargo build --release
: build a release (optimized) version of a projectOften,
cargo check
is much faster thancargo build
, because it skips the step of producing an executable. If youβre continually checking your work while writing the code, usingcargo check
will speed up the process! As such, many Rustaceans runcargo check
periodically as they write their program to make sure it compiles or they depend onrust-analyzer
(sometimes not accurate as exprienced in big blockchain codebase like substrate wheretarget/
folder is 25 GB+) Then they runcargo build
when theyβre ready to use the executable. -
$ cargo build --target <target-name>
: build for a specific target. E.g.$ cargo build --target aarch64-apple-darwin
-
$ cargo build --target <target-name> --release
: build a release version for a specific target. E.g.$ cargo build --target aarch64-apple-darwin --release
-
$ cargo build --target <target-name> --bin <bin-name>
: build a specific binary for a target. E.g.$ cargo build --target aarch64-apple-darwin --bin hello
-
$ API_KEY=... cargo build
: set an environment variable for a project. E.g.$ API_KEY=... cargo build
at compile time. Here, no need to load env vars using dotenvy:dotenvy::dotenv().ok(); let api_key = std::env::var("API_KEY").expect("API_KEY must be set");
Instead, just use
env!
macro:let api_key = env!("API_KEY");
And
API_KEY
is set during compile time. For proper error handling & analyzer linting, use this code inbuild.rs
at the project root:// build.rs fn main() { // Get the API key from the build environment if let Ok(_api_key) = std::env::var("API_KEY") { // Inject the environment variable as a compile-time constant // println!("cargo:rustc-env=API_KEY={}", api_key); } else { panic!("'API_KEY' environment variable must be set at compile time."); } }
Usage: In a wasm environment, the API_KEY is set at compile time, like in Frontend project - Dioxus.
-
$ cargo test
: run all the tests in a project & also captures the output by default. That means output fromprintln!
won't be shown when run. -
$ cargo test -- --nocapture
: run all the tests in a project by keeping the output (i.e.println!
statements) hidden. That means output fromprintln!
would not be shown when run by default as tests are not meant for it, rather to checkassert..
statements. But, using this flag-- --nocapture
, the output is shown. -
$ cargo test <module-name>::<test-function-name>
run a specific test function in a module. E.g.$ cargo test tests::test_add_two_and_two
for rust code:#[cfg(test)] mod tests { #[test] fn test_add_two_and_two() { assert_eq!(4, add_two_and_two()); } fn add_two_and_two() -> i32 { 4 } }
- if there is a single or a few test function(s) then it is better to use
#[test]
attribute. But, if there are many test functions, then it is better to use#[cfg(test)]
attribute to put into module.
- if there is a single or a few test function(s) then it is better to use
-
Publish a crate to crates.io via
$ cargo publish
- Each crate has a limit of 10 MB in size for each version.
$ cargo publish --dry-run
won't upload, just check if everything is fine.$ cargo publish
will upload the crate to crates.io- crates can't be deleted, but can be yanked i.e. a particular version can be removed from crates.io
$ cargo yank --version 1.0.1
will yank the version1.0.1
from crates.io$ cargo yank --version 1.0.1 --undo
will undo the yank
cargo owner
can add/remove owner (individual or team)
cargo owner --add github-handle cargo owner --remove github-handle cargo owner --add github:rust-lang:owners cargo owner --remove github:rust-lang:owners
-
Add workspace member(s) to manage multiple projects inside one rust project (containing
Cargo.toml
file):$ mkdir hello
$ cd hello
$ touch Cargo.toml
- Add this to
Cargo.toml
file:
[workspace] members = ["project1", "project2", "project3"] resolver = "2"
Here,
resolver
is added to ensure that all the members/projects follow rust 2021 or else, set to "1".$ cargo new project1
$ cargo new project2
$ cargo new project3
$ cargo build
: Build the workspace
Now, you would also view the linting in all repos.
-
Make Binary inside lib: Any file (with
main
function) inside a cargo lib project can be defined as binary for easy call viatarget/release/<bin>
For instance, inside a cargo project, the project folder looks like this:
folder structure:
βββ src βββ accumulator.rs βββ bn_solidity_utils.rs βββ client β βββ main.rs βββ constants.rs βββ contracts β βββ Semacaulk.json β βββ Verifier.json β βββ format β βββ foundry.toml β βββ lib β βββ mod.rs β βββ script β βββ sol β βββ tests βββ demo β βββ main.rs βββ error.rs βββ gates β βββ gate_sanity_checks.rs β βββ mod.rs β βββ tests.rs β βββ utils.rs βββ keccak_tree β βββ mod.rs βββ kzg.rs βββ layouter β βββ mod.rs βββ lib.rs βββ mimc7.rs βββ multiopen β βββ mod.rs β βββ prover.rs β βββ verifier.rs βββ prover β βββ mod.rs β βββ precomputed.rs β βββ prover.rs β βββ structs.rs βββ rng.rs βββ setup β βββ main.rs β βββ mod.rs β βββ tests.rs βββ tests β βββ mod.rs β βββ prover_and_verifier.rs βββ transcript.rs βββ utils.rs βββ verifier βββ mod.rs
Now, inside the main
Cargo.toml
file, all the required binary could be added like this:[[bin]] edition = "2021" name = "setup" path = "src/setup/main.rs" [[bin]] edition = "2021" name = "demo" path = "src/demo/main.rs" [[bin]] edition = "2021" name = "client" path = "src/client/main.rs"
Now, you can call
/target/release/client
to call the client program.
NOTE: If there is any error related to
linker
with C, follow this:
You will also need a linker, which is a program that Rust uses to join its compiled outputs into one file. It is likely you already have one. If you get linker errors, you should install a C compiler, which will typically include a linker. A C compiler is also useful because some common Rust packages depend on C code and will need a C compiler.
On macOS, you can get a C compiler by running:
xcode-select --install
Create a .github/workflows/rust-ci.yml
file.
The file should contain this:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set up Rust
uses: actions/checkout@v2
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Build
run: cargo build --verbose
- name: Test
run: cargo test --verbose
- name: Clippy
run: cargo clippy --verbose -- -D warnings
- name: Audit
run: cargo audit
Important ones.
- serde
- serde_json
- thiserror
- eyre
- log & env_logger
- tokio
- ...
- ...
For more, see here.
- calamine | Rust Excel/OpenDocument SpreadSheets file reader: rust on metal sheets
- DSLCad: DSLCad is a programming language & interpreter for building 3D models.
- Implementation of the Ethereum precompiled contracts in Rust
- [A new markup-based typesetting system that is powerful and easy to learn] (https://github.com/typst/typst)
- Twilio Sendgrid Unofficial library to send OTP, tokens to email
- Plotly charts lib for Rust
- LLM-chain
- GeoRust: A collection of geospatial tools and libraries written in Rust
- Blake3 crypto lib (Official)
- SHA3 crypto lib
- Collection of cryptographic hash functions written in pure Rust
Blake3 β
- Easy implementation using GeoUtils
Both formula covered: Vincenty's Inverse Formula, Haversine Formula.
fn main() {
println!("Hello World!");
}
cargo
can also be used for compiling a project like node
in NodeJS project.
rustc hello.rs
or,
cargo build
./hello
βOwnership is Rustβs most unique feature, and it enables Rust to make memory safety guarantees without needing a garbage collector.β
- By default, all the variables are defined as
immutable
equivalent toconst
in JS/TS. - In Rust, borrowing is analogous to referencing in C++ & dereferencing is same as that of C++.
- The value of mutable variable can be changed, but not the type.
- In Rust, every value has a single owner that determines its lifetime.
- Rust has preferred composition over inheritance. That's why in Rust, we use traits to define shared behavior.
- Various sizes of integers, signed and unsigned (i32, u8, etc.)
- Floating point types f32 and f64.
- Booleans (bool)
- Characters (char). Note these can represent unicode scalar values (i.e. beyond ASCII)
usize
: the size is dependent on the kind of computer your program is running on: 32 bits if youβre on a 32-bit architecture and 64 bits if youβre on a 64-bit architecture.
str | String |
---|---|
Primitive Type | Built-in struct |
Doesnβt have ownership of the string as it is typically used by reference | Has ownership of the string |
It is a string slice | It is a growable array |
Size known at compile time | Size is unknown at compile time |
Data allocated in the data segment of the application binary | Data allocated in a heap |
Uses & or reference to assign a str value to a variable | Not need need to use & or reference to assign a String value to a variable |
-
&str
toString
is always an expensive operation, as it is owned with aString
type.let name: &str = "Abhijit Roy" let name_String: String = name.to_string(); // used `to_string()` to convert from `&str` to `String` type
-
String
to&str
is a cheap operation, as it is borrowed with a&str
type.let name: String = "Abhijit Roy" let name_String: &str = &name; // used `&` to convert from `String` to `&str` type
-
The following function is more expensive than the latter.
fn main() { let my_str: &str = "This is a str"; // converting the str to String is an expensive operation print_data(&my_str.to_string()); print!("printing inside main {}", my_str); } fn print_data(data: &String) { println!("printing my data {} ", data); }
fn main() { let my_string: String = String::from("Understanding the String concept?"); print_data(&my_string); print!("printing inside main {}", my_string); } fn print_data(data: &str) { println!("printing my data {} ", data); }
-
- formatting variables inside
println
function
- formatting variables inside
let name = "Abhijit";
let age = 28;
println!("My name is {name}, and age is {age}"); // β
println!("My name is {0}, and age is {1}", name, age); // βοΈ
println!("My name is {}, and age is {}", name, age); // βοΈ
-
- Multiple usage of variables without repetition
let alice = "Alice";
let bob = "Bob";
println!("{0}, this is {1}. {1}, this is {0}", alice, bob);
Here, each aforementioned cases like unused variables, dead code, means something (shown on hover) like this:
This means "allow unused imports". Hence, the compiler will not show any warning for unused variable.
This means "not to allow detecting dead code". Hence, the compiler will not show warning for dead code.
As code example, see this:
//! This won't show any warning for unused import.
#[allow(unused_imports)]
use std::io::{self, Write};
-
Box<T>
- A pointer type for heap allocationBy default, in Rust variables are stored in stack. But, if we want to store in heap, we can use
Box<T>
pointer. This is similar tonew
keyword in JS/TS. -
Box is basically used for:
- For dynamic allocation of memory for variables.
- When there is a lot of data that we need to transfer ownership and we donβt want that they are copied.
Mutex stands for Mutual Exclusion, and it's a synchronization primitive used to protect shared data in concurrent programming. In the context of Rust, the std::sync::Mutex
type provides a mechanism for multiple threads to mutually exclude each other from accessing some particular data.
Let's imagine we have a book π (representing the shared data), and we have two friends π₯ (representing two threads). They both want to write in the book π at the same time.
The book π is our shared data that both friends π₯ want to modify. We can't have both friends π₯ writing in the book π at the same time, because that would create a mess. We need some way to ensure that only one friend π₯ can write in the book π at a time. That's where our Mutex comes in!
A mutex is like a key π to the book π. When a friend π₯ has the key π, they can write in the book π. When they're done, they give the key π back, and the other friend π₯ can take the key π to write in the book π.
Here is how it works in code:
use std::sync::Mutex;
let m = Mutex::new(5); // Here we are creating our book π with a number 5 inside it.
{
let mut num = m.lock().unwrap(); // One of our friends π₯ takes the key π.
*num = 6; // They change the number in the book π from 5 to 6.
} // The friend π₯ gives back the key π when they are done.
println!("m = {:?}", m); // Now the book π has number 6 inside it.
In this example, the friend π₯ is able to take the key π (acquire the lock) by calling m.lock()
. They can then modify the data (write in the book π) by dereferencing num
to get access to the data. When they're done, they give the key π back automatically because Rust's scoping rules will drop the MutexGuard
(represented by num
) at the end of the scope, which releases the lock.
It's important to remember that if a friend π₯ forgets to give back the key π (doesn't release the lock), the other friend π₯ will be left waiting indefinitely, unable to write in the book π. This is called a deadlock, and it's a common problem in concurrent programming that you should try to avoid.
This is a simplified view of mutexes in Rust, but hopefully, it helps you understand the basic concept. For more complex scenarios, you'll want to learn about things like error handling with Result
, using Condvar
for condition variables, and understanding the different methods available on Mutex
and MutexGuard
.
Stack (fixed size like char, bool, int, array; less costly; quick to access by calling var like easy to copy the var)
Heap (variable size like string, vector, class; more costly; access var or object via pointer)
- The memory of the declared variables are dropped (or freed) when the program leaves a block in which the variable is declared.
- E.g. Normally, inside the
main
function, whenever a variable is defined, it is dropped after exiting themain
function.
- E.g. Normally, inside the
fn main() {
// Case-1
let x = 10;
let r = &x;
let k;
{
let y = Box::new(5); // Using Box pointer for storing into heap
let y = 5; // stored in stack
// let y <'a> = 5;
// k = &y; // y dropped here as it is not available for lifetime. Moreover the block is getting over after this
k = y; // this implies that the ownership of 5 is transferred to `k` from `y`
}
}
pub
: Publicpub(crate)
: Public within the cratepub(super)
: Public within the parent modulepub(in path)
: Public within the specified path- ``: Public within the current module
Expand details:
In Rust, visibility modifiers control the accessibility of items (such as functions, structs, enums, etc.) in your code. These modifiers help enforce encapsulation and module boundaries, making your code more modular and maintainable. Here's a detailed explanation of the different visibility modifiers in Rust:
The pub
keyword makes an item public, meaning it can be accessed from any module in the crate and from other crates.
Example:
mod my_module {
pub fn public_function() {
println!("This is a public function.");
}
}
fn main() {
my_module::public_function();
}
In this example, public_function
can be accessed from the main
function because it is declared as pub
.
The pub(crate)
modifier makes an item visible throughout the current crate but not outside of it. This is useful for sharing functionality within the crate without exposing it to external crates.
Example:
mod my_module {
pub(crate) fn crate_public_function() {
println!("This function is public within the crate.");
}
}
fn main() {
my_module::crate_public_function();
}
In this example, crate_public_function
can be accessed from anywhere within the same crate, but not from other crates.
The pub(super)
modifier makes an item visible to the parent module. This is useful for sharing functionality with the immediate parent module but not more widely.
Example:
mod parent_module {
pub mod child_module {
pub(super) fn parent_public_function() {
println!("This function is public to the parent module.");
}
}
fn parent_function() {
child_module::parent_public_function();
}
}
fn main() {
// The following line would cause a compilation error because the function is not accessible here:
// parent_module::child_module::parent_public_function();
}
In this example, parent_public_function
is accessible from parent_function
within the parent_module
, but not from the main
function or any other modules outside parent_module
.
By default, items in Rust are private to the module they are defined in. This means they can only be accessed from within the same module.
Example:
mod my_module {
fn private_function() {
println!("This function is private.");
}
pub fn call_private_function() {
private_function();
}
}
fn main() {
// The following line would cause a compilation error because the function is private:
// my_module::private_function();
// This works because call_private_function is public and calls the private function internally:
my_module::call_private_function();
}
In this example, private_function
can only be accessed within my_module
, but not from main
.
pub
: The item is accessible from any module in the crate and from other crates.pub(crate)
: The item is accessible from any module within the same crate, but not from other crates.pub(super)
: The item is accessible from the parent module.- Default (private): The item is accessible only within the same module.
These visibility modifiers help you control the scope and encapsulation of your code, allowing you to design robust and maintainable Rust programs.
Here are some of the key guidelines for writing comments in Rust:
- Use /// for documenting items such as functions, structs, enums, and modules. The comment should describe what the item does, its parameters (if any), and its return value (if any).
- Use //! for documenting the crate root. This comment should provide a brief overview of the crate's purpose and functionality.
- Use // for adding comments to individual lines of code. These comments should explain what the code is doing and why it's necessary.
- Use Markdown formatting to add emphasis, headings, lists, and links to your comments.
- Keep your comments concise and to the point. Avoid unnecessary details or redundant information.
- Use proper spelling, grammar, and punctuation in your comments.
- Update your comments when you make changes to your code. Outdated comments can be misleading and confusing.
This is important while importing modules.
-
super
is used to import from parent module of the current module (file). When you usesuper
for importing, you're specifying a relative path from the current module's parent.// Assuming we have a module hierarchy like this: // my_module // βββ sub_module1 // β βββ sub_module1_1 // β β βββ some_file.rs // β βββ MyStruct.rs // βββ sub_module2 // In some_file.rs use super::MyStruct;
-
crate
is used to import from root module of the current module (file). When you usecrate
for importing, you're specifying an absolute path from the root of the current crate (where Cargo.toml file is there).// Assuming we have a module named `my_module` at the root of our crate use crate::my_module::MyStruct;
$ cargo init --lib <name>
creates a lib$ cargo init <name>
creates a package
All tests like unit, integration tests are in tests
folder.
-
add
panic
in test to fail the test#[test] #[should_panic] fn fail_creating_weightless_package() { let sender_country = String::from("Spain"); let recipient_country = String::from("Austria"); Package::new(sender_country, recipient_country, -2210); }
-
add
#[ignore]
to skip the test.
To make your rust project WASM compatible for:
- low-power devices which supports WASM binary.
- blockchain code in WASM format for modern blockchains (unlike EVM).
- Web App running on browser. WASM file runs on it as the browser supports WASM.
I had an experience where bson
crate (used in MongoDB DB for supported data types) was used as project dependency & then when the project was used as dependency of web-app
(Dioxus project), it couldn't compile throwing error:
error: the wasm*-unknown-unknown targets are not supported by default, you may need to enable the "js" feature. For more information see: https://docs.rs/getrandom/#webassembly-support
--> /Users/abhi3700/.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.15/src/lib.rs:342:9
|
342 | / compile_error!("the wasm*-unknown-unknown targets are not supported by \
343 | | default, you may need to enable the \"js\" feature. \
344 | | For more information see: \
345 | | https://docs.rs/getrandom/#webassembly-support");
| |________________________________________________________________________^
error[E0433]: failed to resolve: use of undeclared crate or module `imp`
--> /Users/abhi3700/.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.15/src/lib.rs:398:9
|
398 | imp::getrandom_inner(dest)?;
| ^^^ use of undeclared crate or module `imp`
Compiling darling v0.20.10
For more information about this error, try `rustc --explain E0433`.
The solution came out to be:
op-sdk-rs = { git = "https://github.com/abhi3700/omnipay-sdk-rs.git", rev = "ba99af7c2cac9299c7364c87ba5f9457c6db9f7c"}
+ # WASM support for π by overriding.
+ getrandom = { version = "0.2.15", features = ["js"] }
Take care of the
getrandom
version which might have to be upgraded as soon as theop-sdk-rs
is updated.
Hence, issue was resolved! π.
Picked from this book: Rust Design Patterns
Look for files with _opt
suffix like this at the repo root:
β― find . | grep _opt
Use this tool to get the code related issues (if any).
Simple way to install:
rustup component add clippy
Run this to get the issues:
cargo clippy
Video source πππππ
-
stack is used for:
- primitive types
- function param, return values (address), local variables
-
For 64-bit machine, total allowed stack size for the main thread is 8 MB. In below example, there are different stack frame created. Consider the entire white area as stack size for main thread as 8 MB. Also, there is only 1 thread running. Here, stack pointer is getting incremented/decremented based on the program logic running in the thread.
Here, the blurred one is not deallocated, but just be replaced by the next function.
&String -> &str
&Vec<T> -> &[T]
&Box<T> -> &T
format!("{} World!", s1)
let s = "Abhijit is a good boy"; // collection of bytes like
// [65, 98, 104, 105, 106, 105, 116, 32, 105, 115, 32, 97, 32, 103, 111, 111, 100, 32, 98, 111, 121]
let v = s.bytes().collect::<Vec<u8>>(); // RECOMMENDED for iteration
let x = 5;
println!("x = {}", x);
- Coherence/Overlap rule: There should not be multiple implementations using
impl
for the same type. - Orphan rule: The trait or type should be defined in the same crate as the implementation.
β
// crate_a
struct MyStruct;
trait MyTrait {
fn my_fn(&self);
}
β
// crate_b
use crate_a::{MyStruct, MyTrait};
// WRONG: Orphan rule violation
impl MyTrait for MyStruct {
fn my_fn(&self) {
println!("Hello");
}
}
This would be illegal under the orphan rules, because both MyType
and MyTrait
are foreign to Crate B
. If this were allowed, and Crate A
also provided an implementation of MyTrait
for MyType
, there would be a conflict, and Rust wouldn't know which implementation to use. The orphan rules prevent this situation from arising.
- Check behind-the-code for a code snippet - https://play.rust-lang.org/
- Tools >> Expand Macros
- Debugger tool
-
- VSCode Extension
- CLI Installation:
git clone https://github.com/SeaQL/FireDBG.for.Rust.git cd FireDBG.for.Rust cargo install --path ./command
-
- Best 2:
- Rocket (good docs) [Familiar]
- Actix_web (under development) [Recommended]
2
is much faster than1
in terms of performance. Infact, it is closer todrogon-core (in C++)
- Cause: there is a statement having no effect
- Solution: Assign the variable to
_
.
Before:
let result = match grade {
"A" => { println!("Excellent!"); },
"B" => { println!("Great!"); },
"C" => { println!("Good"); },
"D" => { println!("You passed"); },
"F" => { println!("Sorry, you failed"); },
_ => { println!("Unknown Grade"); }
};
result;
After:
let result = match grade {
"A" => { println!("Excellent!"); },
"B" => { println!("Great!"); },
"C" => { println!("Good"); },
"D" => { println!("You passed"); },
"F" => { println!("Sorry, you failed"); },
_ => { println!("Unknown Grade"); }
};
// result; // warning: path statement with no effect, Solution --> assign to `_`
let _ = result;
- Cause: It simply means that the variant is never used, "constructed", anywhere in your program. There is no
AppAction::Task
anywhere in the program. Rust expects that if you say an enum variant exists, you will use it for something somewhere. - Solution: by putting this before the enum, or individually before intentionally unused items, you can make the warning disappear:
Before:
enum UsState {
California,
Mexico,
Alaska,
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
Custom(UsState),
}
After:
#[allow(dead_code)]
#[derive(Debug)] // this use is recommended, otherwise there is error.
enum UsState {
California,
Mexico,
Alaska,
}
#[allow(dead_code)]
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
Custom(UsState),
}
- Cause: Copy designates types for which making a bitwise copy creates a valid instance without invalidating the original instance.
This isn't true for String, because String contains a pointer to the string data on the heap and assumes it has unique ownership of that data. When you drop a String, it deallocates the data on the heap. If you had made a bitwise copy of a String, then both instances would try to deallocate the same memory block, which is undefined behaviour.
- Solution: Just use
format
like this:
Before:
impl Detail for Car {
fn brand(&self) -> String {
return self.brand;
}
fn color(&self) -> String {
return self.color;
}
}
After:
impl Detail for Car {
fn brand(&self) -> String {
// using `format` instead of directly returning the brand bcoz it throws error:
// "move occurs because `self.brand` has type `String`, which does not implement the `Copy` trait"
return format!("{}", self.brand);
}
fn color(&self) -> String {
return format!("{}", self.color);
}
}
Cause: Because of type mismatch
Solution: Just typecast it as the required type
res.push(i as i32);
5. error: the 'cargo' binary, normally provided by the 'cargo' component, is not applicable to the '1.70.0-aarch64-apple-darwin' toolchain
- Cause: It happens on a particular rust codebase. In my case, it happened with
aptos-core
repo. - Solution: Just remove & then add
cargo
via this: Source
1. rustup component remove cargo
2. rustup component add cargo
- Cause: The index is locked by another process. This mainly happens when
$ cargo run
is executed after opening a rust project in VS Code while$ cargo check
is being run byrust-analyzer
extension. Try to run any command afterrust-analyzer
is completed. - Solution: If after long wait, it doesn't go away, then just delete few files:
rm -rf ~/.cargo/registry/index/* ~/.cargo/.package-cache
cargo clean
And then run the command $ cargo build
/$ cargo check
/$ cargo run
again. It would work fine now π.
-
Cause: This is a common issue when trying to update the channel to latest nightly version. This is because the
Cargo.lock
file uses 2 versions ofahash
which conflicts. This can be confirmed by running$ cargo tree -i ahash
. -
Solution: Just follow the steps below:
- Remove your
Cargo.lock
file or you can just remove either of the versions (preferably older). - [OPTIONAL] Clean the
target/
folder using$ cargo clean
. - Build again
$ cargo build
.
- Remove your
- Cause: It might happen that if you install some package using
brew
likematurin
(a project management tool in Python) uses rust. They essentially install/overriderust
with that of homebrew. You can verify if yourrustc
is being called by rust binary ofbrew
like this:
$ which rustc
/opt/homebrew/bin/rustc
You might find these errors when compiling rust project:
error[E0554]: `#![feature]` may not be used on the stable release channel
...
- Solution: Just remove/uninstall
rust
frombrew
(if found in$ brew list | grep rust
). Secondly, remove the package that are dependents likematurin
(in my case). Next, check$ which rustc
.
where rustc
/opt/homebrew/bin/rustc
/opt/homebrew/bin/rustc
/opt/homebrew/bin/rustc
/Users/abhi3700/.cargo/bin/rustc
Delete /opt/homebrew/bin/rustc
via $ rm -rf /opt/homebrew/bin/rustc
. Now, check it should be called by that of .cargo
binary instead of brew
.
$ which rustc
/Users/abhi3700/.cargo/bin/rustc
Now, cargo clean
>> cargo build
should work fine in rust project.
Fixed π.
There is a section called quiz in this repo. It contains some questions and their solutions. The plan is to add them into Rustlings later in an organized manner.
- The Rust Programming Language
- The Rust Reference
- Rust by example
- Rust Cookbook
- Learning Rust With Entirely Too Many Linked Lists
learn by implementing a Linked List in series of chapters.
- Effective Rust
35 ways to better your rust code like "Effective C++" book.
- Learn Rust Documentation
- The Little Book of Rust Macros
- Rust by Practice
- Asynchronous Programming in Rust
- Async programming in Rust with async-std
- The Embedded Rust Book
- Rustlings | Play like a game to learn Rust
- Rustlings course | By JetBrains
- Much more detailed than the above one. As in, they take you through every rust topics & then give you the exercise.
- 24 days of Rust
- Idiomatic Rust
-
Rust for Rustaceans by Jon Gjengset
purchased on Kindle
-
Programming Rust: Fast, Safe Systems Development by Jim Blandy, Jason Orendorff
- Series:
- BecomBetterProgrammer
- TMS Developer Blog (has blogs on Rust full-stack, solana, etc.)
- Learn Rust by KODERHQ
- Hashrust Blogs
- LogRocket Blogs
- This week in Rust
- Possible Rust
- Learn Macros In Rust like Rustlings game
- Learn Rust by aml3
- Rust for C++ programmers
- Rust for Haskell Programmers!
- What is Rust and why is it so popular?
- Understanding the Rust borrow checker
- No auto type deduction for function, but for local variable
- Including Files and Deeply Directories in Rust
- Understand Rust Ownership model by thoughtram
- Memory Safety in Rust: A Case Study with C
- Ownership in Rust by thoughtram
- References in Rust by thoughtram
- Iterators in Rust by thoughtram
- Lifetimes in Rust by thoughram
- Creating a Rust Web App with Rocket and Diesel
- Understanding Rust generics and how to use them
- Understanding lifetimes in Rust