Introduction
I wrote this as a quick guide for folks who are choosing to, or being asked to:
- Work with Rust repositories.
- Align development practices around Rust repositories.
- In some way incorporate Rust in their day to day development considerations.
There is no expectation that you "want" to do any of this, and it's not my goal to make you want to.
The goal here, is to provide a quick tour of what exists in the Rust Ecosystem. I am not intending to teach people how to write Rust. Where possible, I do point to far better resources on that subject than I would be able to write.
The Rust ecosystem has been good to me, and I recommend you give it a shot.
-
For developers coming from C or C++, you should prepare to learn to trust your compiler warnings by default again while learning Rust.
- There will come a day when you know Rust better than the Rust compiler does, but it's probably not going to be Day 1.
-
For developers who don't naturally start with Test-driven Development, I encourage you to prepare yourself to engage with the test system earlier, rather than later.
- The testing framework is already setup for you, and extremely easy to use.
- The benchmark tooling is very easy to access (and will soon be built in).
First Steps
Clone This Repository
- The expectation, is that you have the repository that hosts this book open and available for reference.
git clone https://github.com/canardleteer/quickly-explore-rs.git
Install Rust
- Start by going to rustup.rs to download the installer.
-
You are welcome to audit the shell script beforehand, since I don't recommend randomly piping curl to a shell (but that seems to be the way people do things now?)
-
This sometimes fails for people. There's enough info in the shell script to manually do it.
-
Poke around a little bit.
-
# Take a look at what ya got:
rustup show
# Where did a bunch of stuff land?
rustup which cargo
# Install the Rust Formatter
rustup component add rustfmt
# Install the Rust Linter
rustup component add clippy
Cargo
cargois the general package manager for the Rust ecosystem, and your general "entrypoint" into Rust.- It is also how you launch compiling your code (which is a package, or collection of packages).
- The overall Cargo documentation is going to be better than mine. This is just a quick tour.
- In practice, calling the Rust compiler (
rustc) directly, is rare.
# What version did ya get?
cargo --version
# What can cargo do?
cargo --list
You are now generally setup with everything you should need.
If you want to:
- Learn more about the package manager (Cargo), I recommend starting with the Cargo Book
- Learn more about writing in Rust, the language, I recommend starting with the Rust Book
- Just learn some basics? Keep reading.
VSCode
If you open this repository in VSCode, you will probably want to install the following Extensions:
And possibly whatever else comes recommended.
VSCode is fairly common as a Rust IDE, but you are of course welcome to choose whatever you like...
If you don't want to use VSCode
I fully support this decision.
I hear the emacs experience is great!
I caution you, that as far as I know, neither Rust Rover, nor Sublime implement the Language Server Protocol out of the box. You definitely want to be setup with it for a smooth "start learning" experience.
- It is up to you, if learning how to add LSP support to the IDE of your own choosing, for a language you're just learning, is something you want to do right now.
This Repository
I made this repository with:
cargo new quickly-explore-rs
It made a few files:
Cargo.toml- Which defines your package's dependencies and a few other things.
.gitignore- Which ignores the
targetdirectory, which is your build output.
- Which ignores the
src/main.rs- Which is the source code for this application.
Files that may get generated for you as you poke around:
target/*- Your build directory.
Cargo.lock- Dependency reification lock file - documentation
- If you're authoring an application, yes you commit it to your source tree. If you're authoring a library, no, you don't commit it.
- Even though this repository is an application, we are not committing it, so it remains closer to "as generated."
cargo won't make the following files present in this repository:
README.mdbook/rust-toolchain.toml.github/
Those have been added after the initial build-out, for documentation, publishing & pinning purposes.
Welcome to your Curated Hello World Experience
- Open up
src/main.rs.
Cool, it's there. You're welcome to make changes if you wish to personalize your Hello World experience.
- Run it.
cargo run
Alternatively:
cargo run --bin quickly-explore-rs
I really don't like using cargo to run stuff, since it implies that you "need cargo to run what you built"
(you don't) but you will find it handy if you setup multiple binaries in a repository.
So you can run the binary without cargo directly:
cargo build
./target/debug/quickly-explore-rs
- Build a Release Version
You may hate the incredibly slow runtime performance of this Hello World experience (sarcasm), we can speed that up:
cargo build --release
./target/release/quickly-explore-rs
Now we're cookin' with gas! Ship it!
Clean up whatever changes you made (if any)
You can do some quick formatting and linting stuff:
# will automatically clean up your code, where it can
cargo fmt
# will helpfully bark at you as you learn Rust patterns
cargo clippy
These are two tools are useful as pipeline steps for code hygiene verification, but also EXTREMELY USEFUL FOR LEARNING AS YOU GO.
Neither step above should make any changes on the default files in this
repository. It's just good to know they are there and work. cargo fmt,
by default, will format code on disk. If you don't wish to do that,
you'd want to use cargo fmt --check.
Recommendations
The following recommendations are "worth knowing exist," before continuing. They aren't worth doing a deep dive into yet, but you may want to come back to this section later on to learn a little more.
Sparse Indexing
cargo is changing it's indexing protocol, so I recommend performing whatever
action makes sense for you, to use the HTTP indexing by default, and spare
yourself much build slowness, by going here for instructions.
This will probably eventually become a default, if it isn't already.
Toolchain Pinning
rust-toolchain.toml is useful for tool pinning, you can
learn about it here.
An example would be:
[toolchain]
profile = "default"
channel = "1.74.0"
Cargo Workspaces
It's worth knowing about Rust Workspaces, and you can learn about them here.
- If you find yourself wanting to work on more "packages," but remain in a single repository, this is generally what you want.
- These are things that generally "all change together at once."
- These can be very useful while gradually separating isolated components, without forcing it all to happen at once.
Macros
The macro syntax in Rust is both extremely powerful and useful, but can appear
to be a bit arcane and "meta." They are not like C/C++ macros. It will be
awhile before you understand why and where to use macros, and that's before you
understand how to. All that is okay.
You will use them all the time. If you see stuff like this in code:
#[derive(Something)]println!("something")
Or even more broadly, if you see a:
!#
...you are probably using a macro.
Like all Rust things, there is a book you can read to learn more.
Documentation
-
README.mdfiles are used often. -
Most of the "books" are being generated from mdbook, which is useful for "humane documentation" around APIs or features, and even more useful once your README becomes too long. It's generally "just markdown files as input."
- This guide started as a README, and became an
mdbook. - Pretty much by just doing an
mdbook initand moving over sections in the README.
- This guide started as a README, and became an
-
APIs are documented via
rustdoc, and it comes out of the box in Rust. You will find your normal code comments generally land where you'd expect them to in, but you can learn how to make it really nice by... You guessed it, reading therustdocbook, which is here.
Ecosystem Exploration
The root level place folks head to for the Rust Ecosystem is:
- crates.io
- Publishing crates to
crates.iois a well understood topic. - It gets much more complex when dealing with private/internal crate repositories.
- Publishing crates to
- You will add things here to your
Cargo.tomlfile under[dependencies] - Documentation, will be published to docs.rs
- crates in general are versioned and compiled libraries/executables,
built from a package repository.
- It's usually okay to interchange the term "crate" and "package," but they do have specific meaning in some contexts.
- Once published to a public place like
crates.io, a crate can be redacted, but are otherwise immutable.
cargo itself, has it's own ecosystem of tools, like:
- cargo-generate - Templates for Cargo
- cargo-llvm-cov - Code Coverage (I end up using 2 different code coverage tools)
- cargo-audit - Rust specific vulnerability scanner
- ... plus a whole lot more...
What's out there?
The general "go to" place these days to poke around the ecosystem, is:
Below, I've added a few crates that I think are useful to highlight for some types of engineers I often work with:
-
CLI Arg parsing: clap
-
Serialization / Deserialization: serde
-
Awesome Rust is a curated list of useful packages.
-
Asyncronous Rust: async-book
- I mostly use tokio, but there is more than one
asyncruntime available.- Even for microcontrollers, embassy is an example.
- gRPC Services/Clients: tonic (with prost for protobuf)
- REST Services: hyper... Likley something built on top of
hyper. - HTTP Service Calls: reqwest, but likley a client autogenerated via an OpenAPI package (there are a few).
- "Are we web yet?" is a website that generally contains a curated list of traditional service packages.
- I personally don't use this list much, but it's useful to see what's out there.
- tokio-console is a good thing to know about, if you use
tokio.
- I mostly use tokio, but there is more than one
-
Kubernetes:
- Kubernetes management (from Deployment, through Operators & Controllers to Schedulers): kube.rs
-
Tracing / Logging
- Start by learning the log package with
env_logger. - Then learn about Tokio's tracing package.
- Then, like myself, patiently await
tracing v0.2.x - Once you learn how Context propagation works in Rust,
tracingcan be hooked into observability stacks via layering, but it's worth learningtracingfor justasyncRust first.
- Then, like myself, patiently await
- Start by learning the log package with
Testing
Unit/Integration testing is built into Rust, and it's in the book. Use it before digging into frameworks that extend this functionality.
If you want, as an extremely simple example, you can add the following to the bottom of src/main.rs:
#![allow(unused)] fn main() { #[test] fn some_test() { assert!(1==1); } }
Then run cargo test:
-> cargo test
Compiling quickly-explore-rs v0.1.0 (/home/canardleteer/dev/quickly-explore-rs)
Finished test [unoptimized + debuginfo] target(s) in 0.27s
Running unittests src/main.rs (target/debug/deps/quickly_explore_rs-42636154716fb90c)
running 1 test
test some_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
There's a lot more in this space available than that.
Benchmarking
- I don't think Rust has "full support" for benchmarks without additional packages (yet).
- Benchmark tests (via the
#[bench]macro) are a thing in Nightly Rust, these are good for simple things. - I use criterion.rs for non-trivial benchmarking.
- But there is more available, and you can learn about all of this from the Rust Performance Book.
Docker Images
Containerization is used heavily in the Rust ecosystem for building, testing, delivery & deployment.
Install Some Useful Tools
Use rg after install
rg "# Install Some Useful Tools" .
And it should find the Markdown source for this chapter.
Use mdbook after install
To locally view this book:
mdbook serve book
And it would then be serving at:
Code Change Example
Okay, we covered some really high level "rust ecosystem" stuff.
Let's make a code change.
Add a crate
First, let's add the extremely useful clap library to our Cargo.toml. Start
by checking out the crates.io page.
I am pinning the version, to 4.4.11, and adding 2 Features: derive and env.
- "Features" for crates/packages are important to know about, but I'm not going to cover them here. You can read up on them.
-> cargo add clap@4.4.11 --features derive,env
Updating crates.io index
Adding clap v4.4.11 to dependencies.
Features:
+ color
+ derive
+ env
+ error-context
+ help
+ std
+ suggestions
+ usage
- cargo
- debug
- deprecated
- string
- unicode
- unstable-doc
- unstable-styles
- unstable-v5
- wrap_help
- Hopefully that worked for you like it did for me. If not, when you figure it out, please submit a PR to this repository :)
Use the new crate
Now we need to so something useful with this added crate.
Change src/main.rs to be this:
// Import the Parser trait.
use clap::Parser;
// Declare our CLI arguments.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Name of the person to greet
#[arg(short, long, env = "HELLO_WORLD_NAME", default_value="Rust Learning Person")]
name: String
}
// Say the line, robot.
fn main() {
let args = Args::parse();
let name = args.name.clone();
println!("Hello, {}!", args.name);
}
- If you see an inline code warning, ignore the warning.
Run the new version
You can declare a whole lot more with clap, like commands, sub-commands, etc. But we're not going to do any of that.
- Build it:
cargo build
# ignore the compiler warning for now, we will cover that soon.
- Run it this way
./target/debug/quickly-explore-rs --help:
-> ./target/debug/quickly-explore-rs --help
Usage: quickly-explore-rs [OPTIONS]
Options:
-n, --name <NAME> Name of the person to greet [env: HELLO_WORLD_NAME=] [default: "Rust Learning Person"]
-h, --help Print help
-V, --version Print version
Wow, we got a whole lot out of the box from that, and in general those add-ons do what you expect them to.
- Let's try it two ways:
-> ./target/debug/quickly-explore-rs --name "Some Real Name"
Hello, Some Real Name!
-> HELLO_WORLD_NAME="Some Real Name" ./target/debug/quickly-explore-rs
Hello, Some Real Name!
Format the changed code
- Let's check to see if that's good Rust, with
cargo fmt --check:
-> cargo fmt --check
Diff in /home/canardleteer/dev/quickly-explore-rs/src/main.rs at line 6:
#[command(author, version, about, long_about = None)]
struct Args {
/// Name of the person to greet
- #[arg(short, long, env = "HELLO_WORLD_NAME", default_value="Rust Learning Person")]
- name: String
+ #[arg(
+ short,
+ long,
+ env = "HELLO_WORLD_NAME",
+ default_value = "Rust Learning Person"
+ )]
+ name: String,
}
// Say the line, robot.
- Looks like we didn't quite do that too legibly, but let's let the formatter do the work for us, with
cargo fmt.
# Clean it up for us.
-> cargo fmt
# Verify it cleaned it up for us.
-> cargo fmt --check
->
Looking tidy!
Lint the changed code
- Let's see if the code smells right via
cargo clippy:
-> cargo clippy
Checking quickly-explore-rs v0.1.0 (/home/canardleteer/dev/quickly-explore-rs)
warning: unused variable: `name`
--> src/main.rs:22:9
|
22 | let name = args.name.clone();
| ^^^^ help: if this is intentional, prefix it with an underscore: `_name`
|
= note: `#[warn(unused_variables)]` on by default
warning: `quickly-explore-rs` (bin "quickly-explore-rs") generated 1 warning (run `cargo clippy --fix --bin "quickly-explore-rs"` to apply 1 suggestion)
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Oh yeah, I guess we aren't using name. Go ahead and: delete that line, or
follow the linters suggestion of prefixing it with an underscore if you want
to use it for something else.
There are 4 other options, which would be to disable linting of that specific
rule (unused_variables) for:
- That line
- That function
- That module
- The whole package
But I won't go into that. The linter isn't always right, but often is. You have many options that don't involve disabling the linter completely. Don't disable the linter completely. Not using the linter at all is not a reasonable 5th option.
- Run
cargo clippyagain:
-> cargo clippy
Checking quickly-explore-rs v0.1.0 (/home/canardleteer/dev/quickly-explore-rs)
Finished dev [unoptimized + debuginfo] target(s) in 0.12s
That's it! You are now ready to ship your customizable Hello World experience to the world.
Conclusion
Hopefully, you've somewhat learned how to:
- Can build most Rust repositories from source.
- Generally know how to interact with plain Rust codebases.
- Are not afraid of what's going on in a Rust repository.
- Know, in general, where to find packages, and can loosely equate them to another language's packages.
- Know what kind of build / testing / dependency tooling is available out of the box.
- Are curious maybe? Looking to go back and learn more about some of the features skimmed over?