- Async Patterns
- Code documentation
- Memory management and optimization
- Linux distributions
- Protobuf and gRPC
- Optimizations
- Build
- Serde
Workspaces are a good way to organize projects of more than few thousand lines of code. A typical project organization is the following.
your_project/
Cargo.toml
Cargo.lock
crates/
your_project/
sub_crate_1/
sub_crate_2/
sub_crate_3/
...
And your main Cargo.toml
will look like.
[workspace]
members = ["crates/*"]
The name of each directory is equal to the name of the crate.
This blog post provides more details.
cargo install cargo-edit
cargo add <crate_name>
To specify a specific crate version and some features
cargo add tokio@1.9.0 --features full
To remove a crate.
cargo rm <crate>
cargo install cargo-udeps --locked
cargo +nightly udeps
First install ‘cargo-tree’ and use one of the following commands depending on the use case.
cargo install cargo-tree
cargo tree
cargo tree -p <sub-crate>
cargo tree —features serde_json
Dependency resolution (summary of this page)
Rule 1: versions are considered compatible if their left-most non-zero major/minor/patch component is the same. Rule 2: for versions with leading zeros (could be on multiple levels), the rule 1 is applied once the zeros are removed.
Some examples to illustrate these rules:
- 1.0.1 and 1.3.4 are considered compatible.
- 0.1.0 and 0.1.2 are considered compatible.
- 0.1.0 and 0.2.0 are not compatible.
- 0.0.1 and 0.0.2 are not compatible.
When multiple packages specify a dependency for a common package, the resolver attempts to ensure that they use the same version of that common package, as long as they are within a SemVer compatibility range. It also attempts to use the greatest version currently available within that compatibility range.
If multiple packages have a common dependency with semver-incompatible versions, then Cargo will allow this, but will build two separate copies of the dependency.
To set the default log level to info and set the log level to debug for a specific crate.
RUST_LOG=info,<specific_crate>=debug cargo run
cargo build && strace -e 'connect' ./target/debug/<your_app>
To keep strace outputs.
cargo build && strace -e 'connect' ./target/debug/<your_app> > /dev/null
To trace all the connections form the children (threads)
cargo build --quiet --release && strace -f -e 'connect' ./target/release/<your_app>
cargo build && gdb --quiet --args ./target/debug/<your_app>
In the following example we set a breakpoint on the system call "connect" (i.e. socket connect).
cargo build --quiet && gdb --quiet --args ./target/debug/<your_app>
(gdb) catch syscall connect
(gdb) r
Starting your app
...
<stop on the cathpoint>
(gdb) c
... continue
...
(gdb) info threads
... display info on threads
To stop a program right before it exits
(gdb) catch syscall exit exit_group
(gdb) r
cargo add color-eyre
In you main.rs file.
use color_eyre::Report;
fn main() -> Result<(), Report> {
init()?;
// Some code
Ok(())
}
fn init() -> Result<(), Report> {
if std::env::var("RUST_BACKTRACE").is_err() {
std::env::set_var("RUST_BACKTRACE", "1")
}
color_eyre::install()?;
Ok(())
}
- If you want panics and errors to both have backtraces, set RUST_BACKTRACE=1;
- If you want only errors to have backtraces, set RUST_LIB_BACKTRACE=1;
- If you want only panics to have backtraces, set RUST_BACKTRACE=1 and RUST_LIB_BACKTRACE=0.
cargo add tracing tracing-subscriber
use color_eyre::Report;
use tracing::info;
use tracing_subscriber::EnvFilter;
fn main() -> Result<(), Report> {
init()?;
info!("Hello");
let param1 = "First parameter";
let param2 = "Second parameter";
// % --> Display
// ? --> Debug
info!(%param1, param2 = ?param2, "An additional message");
Ok(())
}
fn init() -> Result<(), Report> {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
std::env::set_var("RUST_LIB_BACKTRACE", "1")
}
color_eyre::install()?;
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info")
}
tracing_subscriber::fmt::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
Ok(())
}
RUST_BACKTRACE=1 cargo run
https://rust.godbolt.org
let a = 0..3;
let b = 3..6;
let c = 6..9;
let all = a.chain(b).chain(c);
pub fn values<'a>(&'a self) -> Box<Iterator<Item = &i32> + 'a> {
// ...
}