The repercussions of missing an Ampersand in C++ & Rust
Copying vs Passing by reference
TL;DR
There’s a funny typo that causes someone to copy data instead of “referencing” in C++. Rust is nice because it provides defaults that protect you from some of these “dumb” mistakes. In this example, I’ll go over how the “move by default” can prevent us from introducing this subtle behavior.
Motivation
I originally hesitated to write this because I thought the topic was too “obvious”, but I did it anyways after watching this presentation discussing migrating from C++14 to C++20. I was specifically inspired by a performance bug due to a typo. This mistake is the “value param” vs “reference param” where your function copies a value instead of passing it by reference because an ampersand ( & ) was missing… Here’s a minimum version of the difference below:
// You're the mythical 100x developer void BusinessLogic ( const Data & d ) { // ... } // You're trash that deserves to be replace by AI void BusinessLogicThatCopies ( const Data d ) { // ... }
This simple typo is easy to miss and the penalty won’t matter for people who aren’t performance sensitive (although if you aren’t strongly affected by stuff like this you probably don’t need to be using C++). One could argue that this example is a one-off and no competent C++ developer would make this mistake, but I’ve even seen it in Google codebases (interpret that as you well). There are plenty of linters and tools to detect issues like this (ex: clang-tidy can scan for unnecessary value params), but evidently these issues go unnoticed until a customer complains about it or someone actually bothers to profile the code. The fact that we have to be vigilant about such a minor behavior is exhausting, and that maybe we should design our language to guide us to sensible defaults.
Rust Defaults
I like Rust because it provides a handful of C++ patterns by default. Compared to the Rust hype marketing this benefit is quite small compared to “memory safety” and “fearless concurrency”, but I like the improved ergonomics nonetheless. Adopting performance oriented defaults removes a lot of the weird “gotchas” early on in the C++ learning curve, as well as the toil about having the proper tooling setup. For brevity, I’ll just focus on the concept of C++ ownership ( std::move ) and reference parameters to keep things short.
... continue reading