« Back to home

My first Rust disappointment

Rust is an up-and-coming new programming language which offers compile time safety, automatic memory management, and C-like performance.

It also offers automatic resource management. In Java terms, it takes AutoCloseable, formalizes it more, and makes it ubiquitous throughout the language. When a resource is accessed, it is given a scope of applicability; when the scope ends, the resource is automatically closed. This eliminates problems like unclosed file and database handles.

To make this work throughout the language without annoyance, Rust introduces the ability when calling into another scope to either pass ownership of a value or lend it. If the value’s ownership is passed, the old scope can no longer access it, and the new scope owns it. If the value is borrowed, the called code can no longer access the borrowed value after returning.

With that background, here’s my first disappointment with the language, given that it seems to be trying to do everything right this time.

Yehuda Katz writes:

In practice, the reason this works so well is that most of the time, functions that take values are “borrowing” them. They take a value, do some work with the value, and return. Holding on to the value for longer, for example by using threads, is both uncommon and an appropriate time to think a little bit about what’s happening.

The starting point when writing new functions is to borrow parameters, not try to take ownership. After a little while of programming with Rust, this imposes no cognitive cost; it’s simply the default.

Except it’s not. The syntax for taking ownership is:

fn is_thirties(person: Person) {  
  person.age >= 30 && person.age < 40

For borrowing, it’s:

fn is_thirties(person: &Person) {  
  person.age >= 30 && person.age < 40

This leads me to propose a rule for future language designers:

When designing syntax for a programming language, the safest and most desirable behavior should be the one with the least syntax.

Rust has apparently repeated the mistake Java made with final parameters: it has made the unadorned syntax be the one you shouldn’t do unless you have a really good reason.

I appreciate that Rust’s choice is probably to make the syntax look familiar to C++ programmers, but to me that seems like a really bad tradeoff. The vast majority of programmers aren’t C++ programmers, and I daresay the majority of C++ programmers aren’t capable of using it safely, so optimizing for them is pessimizing for most use of the language.

Sure, it’s only a single character in front of every parameter, rather than the word final, but to me that makes it worse — it’s really easy to miss a single character.

What I think should have been done would have been for Rust to lend parameters by default; taking ownership of a parameter would then require an explicit warning notation, like an ! on the front. But then, that wouldn’t have looked like C. And of course, it’s far too late to change it now.

Yes, this is a very minor issue. It’s just that I’m old enough and cranky enough these days that when I read about a new programming language, I generally only get through a few pages before hitting an “oh my god what were they thinking” moment. (Go error handling. Scala’s arrow fetish and occasionally needing semicolons. Haskell’s responses to trivial errors. CoffeeScript semantic indentation. Perl 6 operators.) Rust is unusual in that I read entire articles and still wanted to try it, which is why the undesirable default behavior of function arguments disappoints me.