While Rust tools and applications can be managed by package managers like Homebrew, most can be installed with Cargo. However, we must then ask: how can we manage to keep track of those tools and applications and update them accordingly? Let’s talk about it.

Disclaimer Link to heading

The original title of this post used the word “binaries” instead of “tools and applications”, but since this post is meant to be a starting guide, I decided to use the latter.

After all, if you are reading this, you are likely used to using cargo install to install tools like gfold (shameless plug). I suspect many users would refer to most of what they have installed via Cargo to be “tools and applications”. Thus, we’ll be referring to what we are managing as “tools and applications”.

One more thing before we continue: folks reading this may prefer using their system’s package manager or something all-encompassing, like topgrade. This guide is aimed at those using Cargo and meant to be flexible with multiple workflows. There is no one way to manage Rust tools and applications. Feel free to pick and choose what you like from this guide.

Choosing to Use the Locked Flag Link to heading

Let’s start by installing gfold with cargo install.

cargo install gfold

This is great and works well. It does especially so for multi-platform use! However, we can modify it a bit by using the --locked flag. What does the --locked flag do?

“By default, the Cargo.lock file that is included with the package will be ignored. This means that Cargo will recompute which versions of dependencies to use, possibly using newer versions that have been released since the package was published. The --locked flag can be used to force Cargo to use the packaged Cargo.lock file if it is available.”

- The Cargo Book

Alright, so whether or not we use the flag is preferential. There are benefits and detriments to each approach, so let’s see what the docs have to say.

“This may be useful for ensuring reproducible builds, to use the exact same set of dependencies that were available when the package was published. It may also be useful if a newer version of a dependency is published that no longer builds on your system, or has other problems. The downside to using --locked is that you will not receive any fixes or updates to any dependency.”

- The Cargo Book

Essentially, the --locked flag ensures reproducible-ish builds by using the dependencies specified in Cargo.lock (if found).

I prefer to use to --locked flag despite missing out on potential bug fixes, performance improvements and security updates to dependencies. This preference stems from my desire to have my experience using the tool or application to match that of the upstream developer’s experience and intentions, which translates to using the dependencies they tested with and included in hosted binaries (e.g. binaries on GitHub Release pages).

For the remainder of this post, I’ll use the --locked flag, but as you are following along, please feel free to drop the flag wherever you’d like.

cargo install --locked gfold

One thing to keep in mind: we can mix and match which binaries we want installed with the flag. It does not have to be all or nothing! We’ll touch on how to do that later.

Ensuring Tools and Applications are Up to Date Link to heading

Now, you have gfold installed, but we have a new problem: how do we make sure it is up to date? We will use the excellent cargo-update to do so. Let’s install it.

cargo install --locked cargo-update

This tool checks if an installed binary (via cargo install) has a newer version available and will update it accordingly. It also has the ability to update all installed binaries (via cargo install) by using the -a/--all as well as force install (i.e. re-install) selected binaries by using the -f/--force flag.

We’ll want the former behavior in order to accomplish our overall goal. Therefore, though we have only installed gfold thus far, let’s update all potential tools and binaries installed.

cargo install-update -a

Before we continue to the next section, let’s say you want to ensure some dependencies are updated with respect to Cargo.lock and others are not. We can use another Cargo subcommand that comes with cargo-update.

cargo install-update-config --enforce-lock <PACKAGE>

With the above command, we can toggle settings for how individual tools and applications are updated. There are more tweaks you can perform and I recommend passing in the --help flag to see what matches your preferences.

Creating a List of Installed Tools and Applications Link to heading

Some people like to maintain state of what their package managers have installed via files.

  • For example, you can use Brewfiles, which are leveraged by homebrew-bundle to bundle non-Ruby dependencies for use with the Homebrew package manager.
  • Another example: you can execute dnf repoquery --userinstalled --queryformat "%{NAME}" > packages.txt on Fedora to display a list of user installed packages with the dnf package manager and redirect the output to a text file.

Similarly to the dnf example, we will manage a crates.txt file that contains out installed tools and applications. When doing so, let’s use the multi-platform ripgrep instead of grep for our Windows friends.

cargo install --locked ripgrep

Side note: For Windows and PowerShell users, I have been using pwsh on macOS in addition to zsh throughout this post. I’ve tried to ensure multi-platform snippets and PowerShell-specific snippets work on your system, but I apologize for any inefficiencies or missed edge cases you may encounter.

Now, we can parse through the output of cargo install --list in order to create a clean list of every installed tool and application by name.

cargo install --list | rg -o "^\S*\S" > crates.txt

Look below for the contents of my crates.txt on the machine I’m writing this post on. Yours will likely look different!

bat
bottom
cargo-audit
cargo-bloat
cargo-expand
cargo-fuzz
cargo-msrv
cargo-nextest
cargo-udeps
cargo-update
cargo-watch
cargo-whatfeatures
cross
exa
fd-find
ferris-fetch
gfold
git-cliff
kubie
nu
onefetch
procs
ripgrep
starship
tokei
tokio-console
toml2json

We can put this file somewhere safe, like a public or private GitHub repository, in order to share it across systems. Homelab users may consider using rsync or a similar tool to manage file sharing and data backup themselves. The possibilities are endless!

Let’s say we have a new system and want to install our Rust tools and applications from crates.txt. On macOS, Linux and similar systems, we can use xargs to install everything.

xargs cargo install --locked < crates.txt

On Windows and systems using PowerShell, we can do something roughly equivalent, though not quite the same.

$crates = Get-Content -Raw -Path .\crates.txt | Out-String
foreach($crate in $crates.split("`n")) { if ( $crate -ne "" ) { cargo install --locked $crate } }

We are also able to continually update our list by using the command we used to originally create the file. With that and the other snippets and tips laid out in this section, we can create, update, back up, install from scratch, and manage what we have installed on our system.

Automating Updates Link to heading

Finally, we can tie this all together with automation. There are a bunch of commands to remember, so adding shell functions or scripts could be a good idea to make our experience better.

For the following example, we are using a zsh function, but transposing similar logic into your shell of choice should work on most popular platforms.

function update {
    if [ "$(command -v rustup)" ]; then
        rustup update
    fi

    if [ "$(command -v cargo)" ]; then
        if [ ! -f $HOME/.cargo/bin/cargo-install-update ]; then
            cargo install --locked cargo-update
        fi
        cargo install-update -a
        cargo install --list | rg -o "^\S*\S" > $SOME_ABSOLUTE_PATH/crates.txt
    fi
}

The above function checks if rustup is installed and updates your Rust toolchains, including Cargo, accordingly. Then, it checks if Cargo is installed, installs cargo-update if it has not been installed, updates all tools and applications installed with cargo install, and then dumps the list of those same tools and applications into a crates.txt file. Whew.

There are a lot of possibilities here for advanced management here. Perhaps, we could explore an opportunity to force updates with for all installed binaries (i.e. cargo install-update -a --force) if the stable toolchain has been updated via rustup update. I say, get creative!

Update Away Link to heading

With this guide, tips and snippets, we are ready to manage our Rust tools and applications!

There are likely areas for improvement and optimizations that we could make here and there, but this guide is meant to get you started as well as to find and inspire methods that work best for you. Go forth and update away!