Big Button - Part 9 - Rust Development Workflow

Wed, Aug 20, 2025

|

5 min read

|

|

Table of Contents

Introduction

đź’» HACKER In the previous steps we simply edited files with pre-defined code, before running some commands in the terminal. This bare workflow may work to simply and blindly follow along, however there are some tools and methods that we can use to help us develop in Rust.

cool relevant image
Figure: Getting ready to set our tooling and workflow

Here we attempt to outline some of these tools and methods. The goal here is to show how to add, test, and iterate our Rust code for this project.

WARNING

A software development workflow can be highly subjective and ever-evolving, where each developer may have his/her preferences, settings, and so on. Being relatively new to Rust embedded development, my advice is to first follow what is outlined here, then try to customize and set your own workflow however fits you.

Tools

Let’s add the following tools to your local development toolbelt on your computer. For each tool, we will briefly mention the tool, outline some notes, and define its importance and usage.

Remember, the goal here is give some foundemental and useful tools for simplicity for and accessibility for some inexperienced Rust developers.

rust-analyzer

This tool is the official Language Server Protocol (LSP) server for the Rust programming language that provides code completion and code navigation capabilities. Aside from the fundamental Rust tools like cargo, rust-analyzer is essential for any effective development.

rust-analyzer typically runs in the background and in parallel with your IDE, constantly watching your keystrokes and code changes. When appropriate, it provides auto-completion hints, and options, while also enabling you and your IDE to effectively navigate your code (i.e. jumping to variable definitions).

Official documentation for rust-analyzer can be found here.

To install rust-analyzer, follow these links:

After installation, rust-analyzer is enabled and started by default when working on Rust code.

bacon 🥓

This tool is a background code checker/linter for Rust that provides real-time error detection and warnings with minimal user interaction. bacon usually is run continuously in a separate terminal, providing immediate feedback as the code changes.

It is highly customizable and integrates seamlessly with your development workflow. Official documentation for bacon can be found here.

To install bacon, execute the following command within your terminal:

Terminal window
cargo install --locked bacon

After installation, bacon is available system-wide. You can now run the following command within your project root directory (Stop bacon with pressing q):

Terminal window
bacon --all-features --job clippy

TIP

probe-rs offers a Visual Studio Code extension that can use the probe-rs CLI tool with VS Code to offer more advanced debugging and monitoring capabilities.


In this tutorial, we will not be focused on using this probe-rs IDE extension, however I encourage you to familiarise yourself with it and see if that is something you are interested in.

Logging Messages

While logging statements may not be the most powerful or most “sexy” debugging tool, they are effective, simple to understand and use, and tried and true!

The concept is simple. Any time you need to see some information at a specific point in your code, you add a logging statement which will show that information in the terminal output during the program’s run.

Specifically speaking, we will use defmt with the following syntax to output logging.

Here is an example code snippet using various different defmt logging statements.

example.rs
// Import the following on top of the file
use defmt::*; // Import all items from defmt
use defmt_rtt as _; // Import defmt_rtt - Real-Time Transfer (RTT) as transport for defmt log
use panic_probe as _; // Ensure that panic messages are sent through the debug probe
let my_variable = 3;
// Use the various logging functions
debug!("A debug level logging message");
info!("This is a simple info level log message");
error!("This message has a variable: {}", my_variable);

For this specific example Rust code, the terminal output would look like this:

[DEBUG ] example.rs:9 : A debug level logging message
[INFO ] example.rs:10 : This is a simple info level log message
[ERROR ] example.rs:11 : This message has a variable: 3

The Workflow ♻️

Setup

  1. Open the project’s root directory in your IDE.

  2. Open one terminal for code warnings/error monitoring.

  3. Run the following command in this first terminal:

    Terminal window
    bacon --all-features --job clippy
  4. Open up a second terminal for building, flashing, and running your code.

Add Changes Continuously

  1. Using your IDE, make some changes to the code or project configurations.

  2. Monitor if those changes raised any Rust warnings or errors in the bacon monitoring terminal.

  3. In the second terminal that is used for flashing and running code, run the following to build, flash, and run the code:

    Terminal window
    cargo run --release
  4. While it is running, watch log messages, the microcontroller, and all the things …

  5. When ready or needed, stop the running code with pressing Ctrl-c in the second terminal.

  6. Repeat these steps until you are done with your code changes

Commit Changes (Saving Your Work)

  1. Use git to add/stage all your changes.

    Terminal window
    git add .
  2. Commit the changes to the project’s git history with a useful message. Feel free to use conventional git commit messages.

    Terminal window
    git commit -m "Completed some amazing work"
  3. Push the local changes to GitHub.com (assuming the default main git branch)

    Terminal window
    git push origin main

That’ll be our simple workflow. Next, we will talk about our embedded framework.