A landscape image

ABQ Notes I: Towards an Idiomatic Ecosystem

12 minute read suggest an edit

As I’m writing this I am sitting in the lobby of the Clyde Hotel in Albuquerque, New Mexico in the days after RustConf 2023. This writeup is the result of the many great conversations I had there.


Writing Idiomatic Rust means following its conventions, pre-established patterns, and techniques. These conventions, which have slowly been established over time, help newcomers and experts to write predictable code.

Why is it so important that your code is predictable, you ask?

Consider a crate I just made up: beach-bytes (it’s bytes but more handsome) that’s essentially a fancy Vec<u8>. I want to support converting my crates’ primary type BeachBytes into various other types. But how do I expose these conversions to people consuming my crate? A function called convert(format) that I tell which format I want?


// like this???
impl BeachBytes {
    pub fn convert(format: &str) {
        match format {
            "json" => todo!("impl")
            "str" => todo!("impl")
        }
    }
}

Luckily, there exists a convention here 1: as_ indicates a free or very cheap conversion - often just casting a pointer or returning a reference to some inner value - while to_ signifies an expensive conversion, and into_ means the function operates on owned values.


// like this!
impl BeachBytes {
    pub fn as_str(&self) -> &str {
        // impl
    }

    pub fn into_json(self) -> serde_json::Value {
        // impl
    }
}

This is great! It means less worrying about naming for me, less reading docs and trying to figure out what does what for my users, and an easier time understanding my codebase for possible contributors!


Conventions reduce the mental overhead for everybody involved.


It’s important to note that conventions don’t appear out of thin air either; they were forged in fire by people with the same problem you are facing. In this way, they are an essential tool for sharing knowledge.
Often, the problems one faces aren’t unique, and some fellow programmers have met them before.
Conventions are like hikers leaving helpful markers along the trail for later hikers (including themselves!) to benefit from.

This is especially true for Rust, where techniques from other languages often don’t transfer too well because of the borrow rules unique design. Idiomatic Rust is a set of practices and rules aggregated by experienced rustaceans to deal with everyday problems elegantly and predictably.

Now, this is all fine and dandy; A lot has been written about Idiomatic Rust Code by people who are way more intelligent and experienced than me. There is little I could contribute to that discussion.

However, I firmly believe that the above definition is missing a key aspect:


A project is far more than just it’s code!


A project often includes CI/CD workflows, configuration files, documentation, a license, etc. These files often tend to cause even greater headaches for programmers than the code itself, and they aren’t even the focus of your work!
And while there are some tips scattered across the internet, every project has its own quirky solutions.

In conclusion, recall this finding we started out with:

Conventions reduce the mental overhead for everybody involved.

The fragmentation I hoped I illustrated above inflicts real pain on consumers and contributors, often making integrating a crate harder. It means consumers can’t fully trust upstream dependencies to not break their builds. Possible contributors have a more challenging time getting up-to-speed with a codebase.

I would argue we should start to share our knowledge, pool our experience, and define what it means for a project to be an Idiomatic Rust Project.

How Do We Get There?

A Common Place

The first order of business should be aggregating the many scattered techniques from across the internet into a shared place. For example, a mdbook in the style of the Rust API Guidelines book. Featuring tips, tricks, guides, links, and a one-stop checklist.

At the same time, we should compile a set of “standard” GitHub action workflows for easy copy-pasting. Of course, people will want and need to customize their workflows, but a starter workflow should serve most users out-of-the-box and can serve as a starting point for people who need more.

Automation

The best conventions are worth little if they can’t be easily enforced. To this end, clippy does a wonderful job for Rust code, and even tools like cargo-semver-checks, cargo-deny, or Swatinem/rust-cache already do a lot of work to ensure your crate is a good citizen of the ecosystem. However, many opportunities remain to build more automation and tooling around Rust.

WG Idiomatic Ecosystem?

Lastly, we need a common space to discuss these efforts, share ideas, and work on solutions. Would it make sense to start a Rust Working Group focusing on making the ecosystem more idiomatic? I think it does!
So are you maintaining a Rust project?

So do you have experiences, setups, or tools you want to share with the community?
Get in touch!

From the not so sunny Albuquerque, New Mexico
Jonas Kruckenberg

Footnotes

  1. Outlined here https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv

  2. Tauri originally was on the side of “MSRV bumps are semver-major” and therefore had to pin a whole bunch of dependencies causing problems all-around. As the side it picked seemed to be the loosing side anyway, the project is going to switch to an N-X MSRV policy with version 2.

  3. clap generally speaking has a MSRV policy of N-6, bumping manually when newer rust versions are desirable