My Day at the Rust Conference - because let's grab all the most hardcore introverts in the world and put them in the same room, why not

Being a part of the technology community means developing relationships with wizards thousands of kilometres away. It is a simultaneously lonely and connected feeling.

And then, sometimes, all those threads converge in a single point. One becomes able to look in the eye and smile at all those distant mentors, friends and co-collaborators we shared our work with.

That place, for me, today, is RustConf.

I'm looking forward to tomorrow, another full day of learning and sharing. But for the souls who couldn't make it, here is a diary of the day's experiences - and, for those who did make it, well, a different perspective from their own may be appreciated.

Artificial Incompetence

No, to Hell with "chronological order". This is the penultimate talk I listened to today, about the crate cargo-mutants. I'm bringing it up first because I thought the technology was really cool. Plus, it's something readers of the blog can probably play with right now!

This is not a new concept. However, this Rust-specific implementation matches harmoniously with the language's robustness.

Running cargo mutants in your command line will immediately summon an imaginary software developer intern who will proceed to butcher your testing suite code and implement horrible bugs. This is expressed by:

  • Replacing operators (such as && with ||, or += with -=)
  • Replacing functions with a return type with an arbitrary variation of that return type.

Example of the latter: get_cute_otters() returns a -> Option<usize>. cargo-mutants will transform calls of this function like this:

- assert_eq!(get_cute_otters(), Some(3));
+ assert_eq!(Some(0), Some(3));
+ // or
+ assert_eq!(None, Some(3));

cargo-mutants will then raise a warning for any mutation attempt that did not break tests and cause them to fail. After all, if randomly slamming the keyboard on your test suite is labeled as "perfectly fine" by cargo test, it may be time for some detective work.

In my opinion, this does not seem like cargo clippy where one should aim for absolutely 0 warnings. After all, replacing make_sure_this_returns_true_otherwise_existence_will_be_erased() with true should keep the tests passing. Rather, it's an invitation to review potential sources of error and look at your implementation with fresh eyes.

Remember, the developer who wrote the code is usually the one also writing the tests. That means absolutely nothing is stopping them from having a blind spot and making the same mistake twice.

Between Sky And Sea

There is this notion in the technology world that user interface designers are the tip of the iceberg - and the part of development the uninitiated are most acquainted with - while the Atlantean civilization beyond mortal understanding at the bottom of the sea (also known as "kernel and compiler developers") stays hidden from view.

There is another world, one which seems to grow year after year. The network architects, the database connectors, the cloud computing specialists - the ones who recite eldritch formulas like "Mongo", "Postgres" or "Kubernetes" and cast the Summon Money spell. (Unfortunately, Summon Greater Money requires making a pact with an archfiend, such as Microgle or Goosoft).

Two of the talks I attended today were about the connection between a massive database and a user-facing customer application:

  • A MongoDB driver to asynchronously carry data from client to server
  • A web interactive map that tracks public transit, called Catenary and which needs to communicate with city APIs that potentially all use conflicting data formats

I was quite awed by what I witnessed. However:

  • Interestingly, while most talks were "here is how you can use this Rust tool/feature in your own project", these two stood out from the lot by being "here is this cool thing we built using Rust". Because the audience of RustConf was composed of Rust developers (I hope), I imagine presenting a project like this would require fighting a little harder to keep the audience's interest.
  • The talks had a "walk me through your call stack" feel, especially the MongoDB one. This is a really deep dive - and therefore technically interesting to anyone with specialized experience wishing to do something like this on their own. But, for those more familiar with different realms of computing, I felt a bit like constantly journeying deeper in a thick forest, and asking "Where did I come from"?

A word of appreciation to the Catenary panelist for having bilingual (English/French) slides in his presentation - I felt honoured by this nod to the culture of my home city (Montréal).

Interlude

Dear people who code during a coding conference: please show mercy. Please indicate which sacrifices are required to satiate your eldritch thirst. Your zeal is beyond human comprehension.

All That Work Because I'm Too Lazy To Press One Key

I truly got to appreciate rustfmt last summer when contributing to the Rust central repository for my Makefile rewrite project. The CI job known as tidy isn't too enthusiastic about my lines of code spanning the entire circumference of Earth in their length.

Today, I got to witness just how many cogs start turning when I mindlessly type ":w" (yes, not Ctrl-S, I know, I'm so 1337) and see all the little words satisfyingly snap into place. First, the talk mentioned that mere strings are not sufficient to direct proper formatting. rustfmt needs to generate its own IR (intermediate representation) - a concept normally reserved to compilers - and have a rough understanding of the code's logic to then output back a fully formatted string sequence corresponding exactly to the parsed, messy string.

There is zero room for error - a formatter changing the way code is interpreted by the compiler, even in an extremely subtle way, would be a sad way to crash the latest Blazingly Fast™ NASA satellite written by an excited Rustacean intern.

Because of this parity requirement, some dilemmas arise:

struct Otter {
  eyes: Vec<Eye>,
  tail: Tail, // Don't worry, the Tail field can be removed later.
  cute: bool,
}

Let's say that this comment is too long and must be moved to the line under or above. But how can you choose? The formatter doesn't understand English. (Unless your compiler is simultaneously a chatbot - opinions on the genius and/or lunacy of such a technology will not be the subject of this post). Compilers don't have to take comments into account, formatters do.

Next, there are macros. They appear in their non-extended state to the formatter, while the compiler sees them in their fully unrolled state. With this mismatch, formatting in such a way that won't cause a difference in the way the compiler parses your code is another challenge.

I really appreciated this talk. It shed light on how complex some of the quality-of-life tools we use can be.

Interlude

Yes. Let's have two conference rooms. One larger than the Sun with hundreds of chairs, and the other requiring a microscope to properly see. And let's put all the more technical panels in the small room. It will fill right up in milliseconds and the nerds will be sent to listen to the other talk.

Hmph. Sorry. Venting over.

Merge And Release, On Stage

I used to think there was an inverse correlation between technical skill and the ability to teach it. All seconds spent learning how to teach are spent not learning the material, are they not?

After the presentation on rkyv, I acknowledge there may be exceptions to this rule.

First, I must admit it took me an unreasonable amount of time until I realized it's called rkyv because it sounds like "archive" when you pronounce it. Before this revelation came unto me, I constantly mispelled it as "rykv" or "rkvy".

Now that this is out of the way, I loved this presentation. I simply must honour the charisma in running cargo publish to ship 0.8 to crates.io live on stage, a version that the panelist worked on for the last 2.5 years.

rkyv is a zero-copy deserialization framework for Rust. A deserialization crate without the deserialization part. A rawer, more performant version of Serde, if you will. Not that the archives it outputs are human-readable, unlike the latter.

The concept itself is extremely simple at its core. Take data (like a struct), shove it in a [u8] (slice of bytes), and unpack later. This sounds so simple I wondered why it wasn't more widespread, until I was presented how uncanny some of the edge cases can get:

  • Absolute pointers like *const cannot be arbitrarily shoved in a slice, as they contain a memory address. Wrap them up nicely in a Box, perhaps? Now, you must deal with machine-specific endianness, because some computers read left-to-right and others right-to-left.

Small tangent: grabbing Arabic numbers from the Arabs and not keeping the right-to-left reading direction (specifically for numbers) was a mistake. Let's say I present to you the number 5XXX(an n number of further X digits). What's that? Can't see the last digits? I'm hiding them with the palm of my metaphorical hand, tough luck. With big-endian (left-to-right), all you know is that it starts with 5, but you're not even sure if that's a 5000 or a 500 000. With little-endian (right-to-left), you know that the number ends with 5 and is therefore divisible by 5. More data collected per digit!

Anyhow. The live-published on stage version, 0.8, fixes this irregularity by enforcing endianness and wrapping of absolute pointers automatically. Go try it in your projects! Perhaps you'll be happy with the performance gains.

I loved the final optimistic note of this talk. The developer promised to release in one year... back in 2022. Then, he was reminded of the often forgotten truth in software development that "80% done is actually 20% done" and got stuck for a while.

But. During all that time, rkyv stayed. It didn't regress, it didn't rot. It just waited for the right time. And that time came, today.

That makes me feel a lot better about my own abandoned on-hiatus projects.

Conclusion

It's not over. I'll be back tomorrow for more fun, more networking, more crab pinching with the hand to fire up the audience. I'll be giving a small "community talk" about my Rustc test suite rewrite project at 14:45 (2:45 PM) in the tiny room connected to the main ballroom. I'd be delighted to see familiar (or new!) faces.

This has been an amazing day.

Reddit: u/oneirical

Discord: oneirical

Unmaking the Makefiles - exorcising the Rust compiler test suite one file at a time

You descend into the pit of exit codes and one-character flags,
The sunken abyss of the Makefiles,
The mill of 11-year-old legacy scripting.
The writhen shell commands serve in adoration,
Immured beneath a rotten run-make directory.
Before you lies the servitor of order,
The cleaver,
The redeemer,
The recreator,
The run-make-support crate.
Vessel of rust-lang,
You are the unmaker,
You bring the rapture,
The murmurs of error logs rise...
The theatre of CI failures begins.

Let's say, theoretically, that you are some average every day nerd who wants to git gud and do a cool techy thing for once. Unfortunately, you have been locked into various educational institutions since the age of 5 and have been constantly at the centre of shallow praise, leading you to believe you do not need to work hard to succeed in life. Now, painfully mediocre compared to the legends writing operating systems in the womb, how will you accomplish this feat?

The answer, of course, is relentless violence.

Before me stood 352 Makefiles, used for various compiler integrity and correctness checks in the master Rust repository. Note the past tense - each day of the summer of 2024, I have hacked away at them without pause, rewriting them in Rust with the use of the run-make-support crate - which also needed multiple extensions to emulate the various features present in the Makefile swarm.

Here is an example of one such rewrite:

# needs-profiler-support
# ignore-cross-compile

include ../tools.mk

COMPILE_FLAGS=-g -Cprofile-generate="$(TMPDIR)"

all:
	$(RUSTC) $(COMPILE_FLAGS) test.rs
	$(call RUN,test) || exit 1
	[ -e "$(TMPDIR)"/default_*.profraw ] || (echo "No .profraw file"; exit 1)

which becomes:

// -C profile-generate, when used with rustc, is supposed to output
// profile files (.profraw) after running a binary to analyze how the compiler
// optimizes code. This test checks that these files are generated.
// See https://github.com/rust-lang/rust/pull/48346

//@ needs-profiler-support
//@ ignore-cross-compile

use run_make_support::{cwd, has_extension, has_prefix, run, rustc, shallow_find_files};

fn main() {
    rustc().arg("-g").profile_generate(cwd()).input("test.rs").run();
    run("test");
    let profraw_files = shallow_find_files(cwd(), |path| {
        has_prefix(path, "default") && has_extension(path, "profraw")
    });
    assert!(!profraw_files.is_empty(), "no .profraw file generated");
}

All progress was neatly catalogued in this issue, acting as a Most Wanted hitlist of sorts for the most resilient of Makefiles. As of writing this, 11/352 Makefiles remain, which mean the porting efforts are 97% complete. This project was sponsored as part of the Rust Foundation's first year entry into Google Summer of Code.

Due to the extremely commit-spammy nature of this work, this project has rocketed me to - at the time of writing - position #153 of all time in the Rust Contributors Leaderboard. I even soared past my mentor jieyouxu, who is an established maintainer. Anyone with two neurons and a functional synapse in between will understand that this is laughably meaningless, especially considering they made the #1 a bot to remind the monomanic maintainers to touch grass and stop burning themselves out. But, hey, it might deceive some foolish figure of authority one day to give me some respect.

Now that your attention is starting to wane, let me proceed right away to stories of various fun moments throughout this project. I wanted to call this part the "House of Horrors", though it's more a "House of Curiosities" by now.

What Blasphemy Really Looks Like

Let's start with a demonstration of how cursed these Makefiles can get:

ifneq (,$(findstring x86,$(TARGET)))

Source.

What do you think this means? "If the string x86 is not found in the target name, run this block"? WRONG! findstring will return x86 if it finds it in TARGET, and an empty string otherwise. This is compared with the first argument of the expression, (, (did you miss it?), which is an empty string. That means "if TARGET contains x86, then empty string is not equal to x86 (returned by findstring), then run this block." It's the OPPOSITE of my first impression.

Yes, this is real Rust repository code, running on every single pull request merge as part of the test suite, and it has been present there for years.

Until I arrived, of course. You may start applauding now.

Traps and Tricks

Some parts of Makefile syntax truly cut a line between the wheat and the chaff, with the measured quality being an obsessive tendency to read between the lines. Observe:

FLAGS := -C link-args=-Wl,--no-undefined

Source

No whitespace after the comma? Is the formatter broken? Much the opposite: adding a space here would cause --no-undefined to become an extra argument. The test will then crash and burn. You, acute dear reader, have obviously spotted the other whitespace in this string - the third character. That one, as you have guessed with your unmatched intellect, is completely unnecessary:

FLAGS := -Clink-args=-Wl,--no-undefined

Perfectly acceptable.

FLAGS := -C link-args=-Wl, --no-undefined

Cast down a Hell pit to be pinched by crabs for all eternity.

Another fun one:

# Check that a primary bundle can be loaded and will be preferentially used
# where possible.
custom: test.rs working.ftl
	$(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/working.ftl 2>&1 | $(CGREP) "this is a test message"

Source

Please divert your attention for now from the glorious intuitiveness of $< (replaced by test.rs, the first dependency) and 2>&1 (prints out errors alongside normal output). There is something much more evil at play. Normally, RUSTC calls that are expected to fail will be written like this:

	$(RUSTC) broken-file.rs && exit 1 || exit 0

This basically means, "if we run RUSTC and it returns 1 (fail), instead, return 0 (success)". This pattern is extremely common. However, the RUSTC call shown in the bundle test will fail, but has no indication of this. How does the test pass at all? Because the pipe | throws away any resulting exit codes, completely ignoring whether compilation succeeded or failed.

This time, it's supposed to fail, and the error should contain "this is a test message", but a rust program composed of simply println!("this is a test message") would pass this test just as well.

Thankfully, the test rewrite roots out such glaring weaknesses:

fn custom_bundle() {
    // Check that a primary bundle can be loaded and will be preferentially used
    // where possible.
    rustc()
        .arg("-Ztranslate-additional-ftl=working.ftl")
        .input("test.rs")
        .run_fail()
        .assert_stderr_contains("this is a test message");
}

Here is something much less bad, but that launched me into a Schrödinger's cat-approved adventure due to a misunderstanding:

  NAME := $(shell $(RUSTC) --print file-names foo.rs)
  mkdir -p $(TMPDIR)/outdir
	$(RUSTC) foo.rs -o $(TMPDIR)/outdir/$(NAME)
	ln -nsf outdir/$(NAME) $(TMPDIR)
	RUSTC_LOG=rustc_metadata::loader $(RUSTC) bar.rs

The pertinent line is the one starting with ln, which creates a systemic link (basically, a portal leading from one place to another) in the file system. We are creating a symlink from TMPDIR to outdir/libfoo.rlib (NAME is libfoo.rlib), which is itself contained inside TMPDIR. What? This "portal" leads to itself? It's a box containing the box?? A house in which the house is stored???

No, I just forgot that command-line utilities tend to auto-complete the destination, and that the operation is actually creating a symlink from TMPDIR/<any symlink cool name> to outdir/libfoo.rlib.

Final Testination

Porting tests requires opening up a massive amount of pull requests. Pull requests require branches. Branches need to be named. And, so it is said...

There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton

None of that "informative naming convention" nonsense. Names should be cute, not descriptive.

Which led to the glorious rise of:

  • final-testination
  • dwarf-fortestress (on a test about the DWARF data format!)
  • infinite-test-a-novel
  • one-flew-over-the-cuckoo's-test
  • exitestial-crisis
  • bootest-contestllation
  • testigitation-cantrip
  • no-test-for-the-wicked
  • the-intelligent-intestor
  • ...and too many others to list.

The Feline Professionals

Many, many tests involved looking at symbols in an object file, and doing something with them, such as checking that a certain one is present. In the Makefiles, this used the nm command-line utility. How to port this over? "I'll just rewrite nm in Rust", I thought. "it will be so easy."

It wasn't. After a few days, I modestly accepted my fate and used llvm-readobj instead.

Much, much later in the summer, a new contributor arrived and wrote a custom object file traversal to help port a Makefile. I clicked on her website, where I discovered ASCII-art cats and links leading to other sites containing pride flags.

Sometimes, you need to let the professionals do the job.

Assorted Fun Comments

# Check that the compiler errors out when the sysroot requested cannot be
# found. This test might start failing if there actually exists a Klingon
# translation of rustc's error messages.
sysroot-missing:
	$(RUSTC) $< -Ztranslate-lang=tlh 2>&1 | $(CGREP) "missing locale directory"

Source

// This is a test which attempts to blow out the system limit with how many
// arguments can be passed to a process. This'll successively call rustc with
// larger and larger argument lists in an attempt to find one that's way too
// big for the system at hand. This file itself is then used as a "linker" to
// detect when the process creation succeeds.

Source

// Tests are run in alphabetical order, and the second test is dependent on the
// first to set THREAD_ID. Do NOT rename the tests in such a way that `test_run_in_same_thread`
// would run before `spawn_thread_would_block`.
// See https://doc.rust-lang.org/rustc/tests/index.html#--shuffle

Source

// A very specific set of circumstances (mainly, implementing Deref, and
// having a procedural macro and a Debug derivation in external crates) caused
// an internal compiler error (ICE) when trying to use rustdoc. This test
// reproduces the exact circumstances which caused the bug and checks
// that it does not happen again.
// See https://github.com/rust-lang/rust/issues/38237

Source

A Glimpse

Rewriting 300+ Makefiles is, by all sane metrics, an exercise in tedium. What I did here was repetitive and somewhat detached from the "puzzly" part of programming that most nerds seem to name as the reason why they love what they do. According to everything I know about myself, I should have given up way earlier. I should have said "this sucks" or something like that. But I didn't. I actually liked doing it. I put on some dark ambient music albums, entered a trance state and just did it.

I announced at the beginning of the summer that I had a full time 40 hours/week internship that had unexpectedly sprung up. I completed both it and this open source project simultaneously. How I was able to find the time to fulfill my project and still work full time at a software company without getting burnt out of looking at computer screens will be left as an exercise to the reader.

I was assigned tasks, of course. The main one - avoiding excessive detail - was related to implementing an AI product into their application. The task itself probably has more technological complexity than the Makefiles, even though I have a sneaking suspicion that a sizable portion of people part of a community like Rust are going to be at the very least business-AI-skeptics.

The central point: even though it was more "crunchy" in raw programming terms, I found myself enjoying much more the Google Summer of Code to an extent that's not even comparable. And it's only now that I learn something important:

It's not about the tedium of the task, it's about the context of the task.

Pretty much everyone I talked to throughout the summer has been way more knowledgeable than me, and yet I still felt among equals. I wasn't working for them, I was working with them. At no point did my heart ache with the paranoia I associate with an authority figure prowling about the creaky floorboards of a cubicle maze.

At my "official" workplace, I saw the masquerade of meetings, the theatrics of project managers. I felt like in a playground where the children are replaced by adults pretending to play with the multicoloured slides and swings, their smiles saccharine and hollow.

So much of our world still seems ordered in masters and servants. But, this summer, I saw a glimpse of something different.

The next time a fellow undergraduate student imagines with glee their future in the forges of FAANG/MAMAA and asks me to reciprocate, I'll answer with respectful doubt instead of shared excitement.

Conclusion

I will be in person at Rustconf on September 11-12. I'll be accompanied by an amazing friend whom I'll be seeing in real life for the first time. My excitement is barely contained.

I went from not knowing Rust at all 1 year ago to having my life rewritten in it. Who could have thought the Rewrite It In Rust evangelists had their dominion extend even to people's lives?

If anyone reading this blog is coming to the conference in person, feel free to let me know!

Reddit: u/oneirical

Discord: oneirical