A random assortment of Rust notes

I thought I’d jot down some notes about using Rust that wouldn’t have made it as their own blog posts:

  1. You can’t use anonymous functions for control flow
  2. The build times
  3. Traits, traits, traits, and more traits
  4. Keeping up with Rust news

You can’t use anonymous functions for control flow

In Ruby, you can return from almost anywhere in a function:

def find_even arr
  arr.each {|i| return i if i.even?}
end
#=> :find_even

find_even [1,2,3]
#=> 2

Unfortunately, it also returns nonsense if the inputs break the invariant that an even number exists in the set:

return_even [1,3]
=> [1, 3]

This is due to the expression arr.each { ... } returning arr by default.

In Rust, the return keyword is much more predictable – it returns from the immediate functional scope, regardless of whether it’s an anonymous function or not, meaning you can’t use anonymous functions in Rust the same way you’d use block syntax in Ruby for control flow. So this code, which is roughly equivalent to the Ruby, doesn’t compile:

fn find_even(arr: &[u8]) -> u8 {
    arr.iter().for_each(|i| {
        if i % 2 == 0 {
            return i;
        }
    });
    // Can't return null or self array, so return default 0
    0
}

This also returns nonsense, but at least it’s nonsense during compilation and not during runtime where people might actually see it:

error[E0308]: mismatched types
 --> src/main.rs:4:20
  |
4 |             return i;
  |                    ^ expected `()`, found `&u8`
  |
note: return type inferred to be `()` here
 --> src/main.rs:4:20
  |
4 |             return i;
  |                    ^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `play` (bin "play") due to previous error

This error just means that we’re trying to return a &u8 from within the anonymous function consumed by for_each, and for_each doesn’t expect any such returned value.

If you find yourself in such a bind, break your higher order function out into a control flow mechanism that doesn’t create a new functional scope:

fn find_even(arr: &[u8]) -> u8 {
    for i in arr.iter() {
        if i % 2 == 0 {
            return *i;
        }
    }

    0
}

💡 Note: llogiq mentioned that you can easily replace this with a call to find() to keep using functional style method chaining. Do that! This was just a contrived example.

💡 Note #2: Oliver M. S. from LinkedIn alerted me to the core::ops::ControlFlow trait used with try_* methods like try_for_each that generalizes this kind of thing. Holy shit, it responds to ? like Result and Option? That’s slick. 🔥

The build times

A recent Google blog post contained some great news for Rust: it’s no more or no less difficult to learn than other languages. It also noted some room for improvement:

“Slow build speeds were by far the #1 reported challenge that developers have when using Rust, with only a little more than 40% of respondents finding the speed acceptable.”

Abi Noda, a developer experience evangelist, also recently wrote a newsletter covering a study on build time effects on developer effectiveness. That study found that the relationship between build times and likelihood of a developer completing a task was linear – that is, any reduction in build times is an increase in the likelihood of completing a task.

In my experience, the build times have been noticeable, though my overall experience is that it’s less painful overall than the dice roll that is building nokogiri on a legacy Rails code base.

Still, it’s important to note that you can reduce local buildtimes by a significant proportion just by using sccache. sccache is a caching mechanism by Mozilla that wraps calls to the Rust compiler and speeds up build times by caching compilation artifacts. If you’re using GitHub Actions, sccache has a GitHub Actions’ cache API backend.

Just using sccache in CI netted us a 18% build time improvement. Locally, I saw much better results on the order of 50% build time reductions, probably because it’s not making network calls to a remote caching service.

Traits, traits, traits, and more traits

It’s hard to overstate the importance of traits in Rust. To use Ruby as an example again, the method resolution algorithm goes roughly through these steps:

  1. Is the method defined on the object?
  2. Is the method defined on the class of the object?
  3. Is the method defined on any parent classes?

If so, it will execute the method found on the object, class, or class hierarchy. Basically every time you ask Ruby to perform a task, it shuffles through its pant pockets, then jacket pockets, then its parents’ pockets until it finds something suitable and hands it off to you.

Rust, on the other hand, must know not just that it has the thing you’re looking for, but that the thing you’re interacting with conforms to the exact behavior you’re looking for. Instead of asking “is the method here?” Rust asks “is this the kind of thing you’re even looking for?” To compare:

  1. What type of thing is this?
  2. What trait, or types of behaviors, does it implement?
  3. What trait is being requested? i.e. just because a trait defines the same methods as another one doesn’t mean they are equivalent.

This is far more correct, but can be onerous to deal with. Instead of shuffling through pockets, it’s more akin to registering for a passport. Your data structures are citizens of certain countries and if they apply for a passport and meet the requirements, they can travel to other countries. However, their statuses must be known before their travel privileges are. Pockets will be pre-searched by the Rustc Traits Authority.

Traits are the passports of the Rust ecosystem. If you’re using a third party library, you may need to implement a trait in order to use the library on your data structures. Alternately, a library may lean on traits already implemented in the standard library to operate. Either way, it’s traits all the way down. 🐢

Keeping up with Rust news

I have a short list of places I go to for Rust related news and community:

  1. https://this-week-in-rust.org/ – this weekly newsletter is my favorite avenue for keeping up. It includes the community’s blog posts, which you can submit yourself, community events (in-person and online), a quote of the week which I am especially fond of, and more. If you do nothing else, sign up for this mailing list.
  2. https://old.reddit.com/r/rust/ – I don’t agree with where Reddit is going [0] and I see a lot less activity here than I used to, but it was the most active and dynamic Rust community for a long time and a lot of good discussion has happened on this subreddit.
  3. https://users.rust-lang.org/ – My new haunt for community related stuff. Go here and post! It’s a lot slower, but slow can be good.
  4. https://lobste.rs/t/rust – Lobste.rs is a technical community where a lot of good discussion happens. Also less active than Reddit, but generally the quality of discussion is better.
  5. https://news.ycombinator.com/ – Of course the orange site. Rust is beyond the point where “written in Rust” means an automatic frontpage on Hacker News, and is, in terms of technology almost boring for perpetually online folk like myself, but bigger announcements will often have good technical discussions. You can count on this crowd to start fights about having a Code of Conduct, though. In terms of psychological safety, Rust project (meaning Foundation + community) moderated spaces feel much safer.
  6. Finally, just following #rustlang on Mastodon. I’d say Twitter, but I *sigh* can’t really endorse using Twitter [0].

I don’t do Discord unless I have specific questions, I don’t do video platforms because I don’t have the time and it aggros my kids, and I also don’t do podcasts because there haven’t been any ongoing Rust-specific podcasts that I’ve liked (and I’m aware of Rustacean Station). I did enjoy New Rustacean, which was about learning Rust, and Building with Rust, which is a series of interviews, but they have both unfortunately come to an end, or so it seems.

Another angle to take is not just “where,” but “who.” I find these people worthwhile to follow:

  1. Jon Gjengset – A prolific YouTuber and Rust personality who is renowned for long, in-depth live coding adventures. I will admit, I have not gotten through a single live coding video. Crust of Rust is a great series and I have finished sitting through one of those, however.
  2. Mara Bos – Rust Lang Library team lead and author of Rust Atomics and Locks, Mara’s posts always pop up on my feeds explaining neat new features in Rust releases.
  3. Aria Beingessner / Gankra – author of Learning Rust With Entirely Too Many Linked Lists and author of cargo-dist and oranda at axo.dev, Gankra has a deep understanding of Rust. Make sure to check out their blog: https://faultlore.com/blah/
  4. Andrew Gallant / burntsushi – I struggled to remember burntsushi’s IRL name because they’re so prolific on reddit. They’re the author of the regex implementation in Rust and when they comment online they’re generous with benefit of the doubt and excellent at explaining themselves.
  5. Amos Wenger / fasterthanlime – Amos is another incredibly prolific YouTuber and blogger. Many a time I have loaded up a new fasterthanlime article, glanced at the estimated read time, and realized I would not be finishing the article in one sitting.

There are tons of helpful people in the Rust community – this is just a sample off the top of my head.


[0]: “Pluralistic: Tiktok’s enshittification (21 Jan 2023)” – https://pluralistic.net/2023/01/21/potemkin-ai/

Leave a Reply

Your email address will not be published. Required fields are marked *