In this part of the review, I would like to focus on Rust’s ecosystem: in other words, how Rust plays with other parts of a functioning system and how Rust’s standard library vs. external libraries interact with each other. There are a lot of pieces to cover in these areas and they have left me with mixed feelings. Let’s look at some.
The standard library
std library feels generally well-thought out and full of features.
One specific thing I have found to be neat is the separation of generic concepts from platform-specific features. For example, the
fs module restricts itself to portable APIs that work on any operating system, but you can import platform-specific traits from other modules (like Unix’s variant of
MetadataExt) which magically extends these APIs with those platform’s features. I’ve never observed such seamless integration between generic and platform-specific APIs anywhere else.
On the other hand, it’s sad that basic functionality such as options parsing and logging are not in the standard library. This makes it harder to write command-line tools, which arguably are one of the main things you’d want to write with a systems-oriented language like Rust. And the reason I qualify this as sad is because, by pushing these responsibilities to external libraries, there is no “standard” way to implement these features which results in inconsistencies among tools.
Finally, the standard library is also lacking in some of the most low-level parts. For example: there is no way to change file owners without resorting to an unsafe
libc call or to handle Unix signals in a good manner. Again, these are things you’d expect a systems-oriented language to offer from the get go, but Rust doesn’t do this fully yet.
But maybe I’m being unfair. After all, Rust 1.0 was only published 3 years ago and that release explicitly claimed that the
std library wasn’t “done”.
Nightly builds and unstable APIs
Another “problematic” area are nightly builds and unstable APIs. I haven’t had a chance to experience the pain myself much, but I’ve read many stories on how some tools may require specific nightly builds and some of their dependencies may require other specific versions, making it impossible to build the final product. I have the impression, though, that this is becoming less of a problem these days because people are aware of this issue, and because the standard library has quickly gained many of the missing features that required nightly builds.
What I have had to deal with are unstable APIs. I’ve been a bit surprised by how some functionality I was looking for, which seemed pretty essential, was marked “nightly-only”. The good thing is that these APIs are clearly highlighted in the documentation and the compiler does not allow you to use them unless you explicitly declare their use. In other words: to use unstable features, you must make a conscious choice to do so, and you must specify this in the code.
On the one hand, it’s great that the editor “just works” on a Cargo-based project: no need to fiddle with the manual installation of tools nor any kind of configuration. On the other hand, I’ve found the whole integration a bit unstable. In many occasions, code completion or code hints stop to work and the only way to recover is to restart the RLS.
To conclude, a few words on the “open-source world”, by which I try to refer to the ecosystem of tools that form the foundations of the Unix-like systems we use every day.
Rust feels like a well-behaved player in this area. Rust seems to support the specifics of the underlying operating systems without claiming that they are “wrong”. One example is in what I explained above about the standard library and its ability to expose platform-specific features; the other is in supporting a variety of library styles. As we shall see soon, this is a huge contrast to Go’s approach.