Node to Rust — Day 2: from npm to cargo
December 2, 2021
Introduction
cargo
is Rust’s package manager and operates similarly to npm
from node’s universe. Cargo downloads dependiencs from crates.io by default. You can register an account and publish modules just as you would on npmjs.com. With some minor mapping you can translate almost everything you’re used to in node to Rust.
This guide is not a comprehensive Rust tutorial. It’s meant to bootstrap experienced node.js users into Rust. We’ll take common node.js workflows and idiomatic JavaScript and TypeScript and map them to their Rust counterparts. This guide tries to balance technical accuracy with readability and errs on the side of “gets the point across” vs being 100% correct. When something is glossed over, we’ll add links for those looking to dive deeper.
Post questions and comments to me on Twitter @jsoverson or @candle_corp and join others taking this same plunge on our Discord channel.
Quick links
- Day 1: From nvm to rustup
- → Day 2: From npm to cargo
- Day 3: Setting up VS Code
- Day 4: Hello World (and your first two WTFs)
- Day 5: Borrowing & Ownership
- Day 6: Strings, part 1
- Day 7: Syntax and Language, part 1
- Day 8: Language Part 2: From objects and classes to HashMaps and structs
- Day 9: Language Part 3: Class Methods for Rust Structs (+ enums!)
- Day 10: From Mixins to Traits
- Day 11: The Module System
- Day 12: Strings, Part 2
- Day 13: Results & Options
- Day 14: Managing Errors
- Day 15: Closures
- Day 16: Lifetimes, references, and
'static
- Day 17: Arrays, Loops, and Iterators
- Day 18: Async
- Day 19: Starting a large project
- Day 20: CLI Arguments & Logging
- Day 21: Building and Running WebAssembly
- Day 22: Using JSON
- Day 23: Cheating The Borrow Checker
- Day 24: Crates & Tools
npm
to cargo
mapping
Project settings file
In node.js you have package.json
. In Rust you have Cargo.toml
.
Cargo’s manifest format is toml rather than the JSON you’re used to with npm’s package.json. Cargo uses the Cargo.toml
file to know what dependencies to download, how to run tests, and how to build your projects (among other things).
Bootstrapping new projects
In node.js it’s npm init
. In Rust you have cargo init
and cargo new
.
cargo init
will initialize the current directory. cargo new
initializes projects in a new directory.
Installing dependencies
In node.js it’s npm install [dep]
. In Rust you can use cargo add [dep]
if you install cargo-edit
first. Note: not cargo-add, just in case you come across it.
$ cargo install cargo-edit
This gives you four new commands: add
, rm
, upgrade
, and set-version
Installing tools globally
In node.js it’s npm install --global
. In Rust you have cargo install
.
Downloading, building, and placing executables in cargo’s bin directory is handled with cargo install
. If you installed rust via rustup
then these are placed in a local user directory (usually ~/.cargo/bin
). You don’t need to sudo cargo install
anything.
Running tests
In node.js it’s npm test
. In Rust you have cargo test
.
Cargo automates the running of unit tests, integration tests, and document tests through the cargo test
command. There’s a lot to Rust testing that we’ll get to in a later post.
Publishing modules
In node.js it’s npm publish
. In Rust you have cargo publish
.
Easy peasy. You’ll need to have an account on crates.io and set up the authentication details but cargo will help you there.
Running tasks
In node.js it’s npm run xxx
. In Rust, it depends… You have commands for common tasks but the rest is up to you.
In node.js you might use npm run start
to run your server or executable. In Rust you would use cargo run
. You can even use cargo run --example xxx
to automatically run example code.
In node.js you might use npm run benchmarks
to profile your code. In Rust you have cargo bench
.
In node.js you might use npm run build
to run webpack, tsc, or whatever. In Rust you have cargo build
.
In node.js you might use npm run clean
to remove temporary or generated files. In Rust you have cargo clean
which will wipe away your build folder (target
, by default).
In node.js you might use npm run docs
to generate documentation. In Rust you have cargo doc
.
For code generation or pre-build steps, cargo supports build scripts which run before the main build.
A lot of your use cases are covered by default, but for anything else you have to fend for yourself.
npm
’s built-in task runner is one of the reasons why you rarely see Makefile
s in JavaScript projects. In the Rust ecosystem, you’re not as lucky. Makefiles are still common but just
is an attractive option that is gaining adoption. It irons out a lot of the wonkiness of Makefile
s while keeping a similar syntax.
Install just
via
$ cargo install just
Other alternatives include cargo-make
and cargo-cmd
. I liked cargo make
at first but its builtin tasks became just as annoying as make
’s. I’ve become skilled writing Makefile
s but I wish I spent that time learning just
so take a lesson from me and start there. If you do go the Makefile
route, check out isaacs’s tutorial and read Your makefiles are wrong.
Workspaces & monorepos
Both package managers use a workspace concept to help you work with multiple small modules in a large project. In Rust, you create a Cargo.toml
file in the root directory with a [workspace]
entry that describes what’s included and excluded in the workspace. It could be as simple as
[workspace]
members = [
"crates/*"
]
Workspace members that depend on each other can then just point to the local directory as their dependency, e.g.
[dependencies]
other-project = { path = "../other-project" }
Check cargo-workspaces
in the next section for a tool to help manage cargo workspaces.
Additional tools
cargo-edit
If you skimmed the above portion, make sure you don’t miss out on cargo-edit
which adds cargo add
and cargo rm
(among others) to help manage dependencies on the command line.
Install cargo-edit
via
$ cargo install cargo-edit
cargo-workspaces
cargo workspaces
(or cargo ws
) simplifies creating and managing workspaces and their members. It was inspired by node’s lerna
and picks up where cargo
leaves off. One of its most valuable features is automating the publish of a workspace’s members, replacing local dependencies with the published versions.
Install cargo-workspaces
via
$ cargo install cargo-workspaces
note: workspaces is plural. Don’t install cargo-workspace expecting the same functionality.
cargo-expand
We’ll go into macros in a later post, but know that macros in Rust are so common that 100% of the logic in your first Hello World app will be wrapped up into one. They’re great at hand waving away code you don’t want to write repeatedly but they can make code hard to follow and troubleshoot. cargo expand
helps pull back the curtain.
cargo-expand
needs a nightly toolchain installed which you can get by running
rustup install nightly
Install cargo-expand
via
$ cargo install cargo-expand
Once installed, you can run cargo expand [item]
to print out the fully generated source that rustc compiles.
cargo expand
takes a named item, not a file path. Runningcargo expand main
doesn’t expandsrc/main.rs
, it expands themain()
function in your project’s root. With a common layout, to expand a module found in a file likesrc/some_module/another.rs
, you’d runcargo expand some_module::another
. Don’t worry, we’ll go over the module system in a few days.
If you ran the cargo new
command above to test it out, this is what your src/main.rs
probably looks like.
fn main() {
println!("Hello, world!");
}
println!()
is a macro. Use cargo expand
to see what code it generates.
$ cargo expand main
fn main() {
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["Hello, world!\n"],
&match () {
() => [],
},
));
};
}
tomlq
While not a cargo xxx
command, it’s useful for querying data in .toml
files like Cargo.toml
. It’s a less featureful sibling to the amazing jq
. It’s not critical, but it’s worth knowing about.
Wrap-up
The npm
→ cargo
mapping is straightforward when you add cargo-edit
and accept the lack of a standard task runner. The next post in this series goes over how to get your environment working with Visual Studio Code: Day 3: Setting up VS Code.
You can reach me personally on twitter at @jsoverson, the Candle team at @candle_corp. Don’t forget to join our Discord channel where people are already joining after starting down this same path.