That’s a long post, I’ll try to it justice by replying to as many of your points as time allows:
Recently I had a funny experience, where I spotted someone writing some PowerShell from across the room, but too far away to actually read their code. I could tell from the colour scheme that it was the PowerShell ISE screen, but nothing else specific. I could tell – from that distance – that they were a former VBScripter writing scripts exactly like they used to before, except in a new language. They hadn’t embraced any of the PS “way of doing things”, they were just writing their old VBScripts in the new syntax. On closer inspection, my suspicions were verified, the script they were writing looked like it had been mechanically translated line-by-line from a VB script, despite being new code dealing with Office 365 automation.
Once you learn enough languages, and have sufficient decades of experience under your belt, you can get very good at spotting the “accents” of other developers. My main complaint with Rust is that most of its core developers have a strong “C++ accent”. In my mind, Rust 1.x isn’t Rust enough. You can argue all you want that I’m wrong about this, but to me it looks like C++ 2.0 from a mile away.
Yes! This is why I don’t like the current
str types, especially that the latter is a built-in type like
i32 despite being rather complex under the hood. I don’t like that UTF-8 as a memory layout was basically forced on everybody, when UTF-16 is still all too common with Java, C#, and Win32 interop – none of which is going away any time soon. This is the same mistake the Haskell guys made by forcing a specific string implementation on people when it was clearly wrong. Strings are complex enough to warrant an interface.
Rust should have used a set of string traits only, and set things up from the get-go to be 100% smooth across a range of string encodings, including special cases such as
Iterator<char>, compressed strings , UTF-16, Win32 Latin1, etc…
This doesn’t mean that the standard library has to implement every possible encoding, just that it should have prepared the ground for libraries or
std v2.0 to fill the gaps. Right now, things are… a mess. In an earlier post I highlighted issues such as
a.foo(b) working but
b.foo(a) failing to compile. This is the tip of the design iceberg. It may look small, but it says a lot about what’s going on under the water. Crates won’t fix this.
Someone mentioned that this slim std-lib decision was a necessary evil to ship Rust 1.0, and this limitation has been incorrectly embraced as a virtue by many people. A thin standard library is one of the reasons I abandoned C++. My productivity in other languages is at a minimum 5x better than in C++, and most of that is due to the richer libraries available out of the box.
Unless you mean C or C++, documentation isn’t exactly uncommon… Even there, documentation is often quite good, I used to use Doxygen back in 2001 or so along with many other people. Standardised “comment-based docs” are over 20 years old now, both Java and C# 1.0 had this.
I like that you play devil’s advocate to your own post, that’s very scientific of you. 8)
However, to add the “peer review” aspect to that scientific bent:
Maintenance: In my experience with all third-party module systems such as
npm, or whatever is not so much that the maintenance is hit & miss, but that this is not self-evident. How do you know if a module is maintained or not? How do you know that if there is a tiny but critical problem that you can get pull request merged without actually trying? How do you know that despite a 10-year history the dev has recently gotten a new job and has put tools down? This is bad enough if you pull in a large dependency such as
diesel, but what about all the transitive dependencies? Are they all high-quality? Production-ready? Safe? Maintained? Future-proof? Consistent with other transitive dependencies you’ll be pulling in indirectly via other crates?
Poor Quality: This is a much bigger deal than it sounds. For example, several people have pointed out that short of reading through all code of all transitive dependencies, you have no idea if there is
panicsomewhere in an innocent-looking library that will crash your application process. Neither
cargoproperly handle this. Oh sure, there’s the
unsafekeyword, but this isn’t “bubbled up”, like with C# where you have to mark the entire library as “compile with /unsafe”. I just discovered that the
mmapcrate doesn’t support 32-bit! Err… what? Compared to random landmines like that, I know that Rust’s
stdlibrary has been tested with 32-bit. I know that Microsoft has tested C# with 32-bit. How do I know that every transitive dependency will work on 32-bit platforms if 99% of Rust developers are using 64-bit operating systems to develop their crates?
Incompatibility: This is just goint to get worse. Even some trivial modules are pulling in a dozen or more dependencies, and those in turn are pulling in more, which in turn… ugh. The NPM fiascos have shown that this just leads to madness at scale.
I have a feeling that module systems like
cargo.io simply don’t scale as currently designed. There’s a honeymoon period that just doesn’t last once the real-world kicks in. Based on just observing the mess that is NPM, the following features at a minimum really ought to have been included in Cargo from day #1, but apparently stability and safety just aren’t priorities right now for a language aiming squarely at web developers and systems programmers:
- Code signing or some sort of method for securely verifying the origin of code.
- Some clear – or better yet – enforced way to verify that what’s on
GitHubmatches what’s on
- Some sort of namespace system to avoid typosquatting, and random low-quality crates permanently taking every common dictionary word. There really ought to be an “official” rust library prefix, at the very least. E.g.:
rust-std/uuidor instead of just
uuid, which could be anything written by anyone.
- Some method to handle renames of crates, such as using GUIDs as the real crate identifier, and the display name used only during the initial search.
- Compatibility flags on all crates, such as the required
corecompatible, 32-bit or 64-bit, x86 or ARM, SSE2 or AVX, etc, etc…
- The popularity of a crate (number of downloads, etc…) so you can judge how many people use it.
- Whether it is prerelease or not, including all transitive dependencies.
- Automated builds or unit tests vs various rustc versions, verifying compatibility.
Some people in this thread mentioned that both Java and C# are going down the same path with things like
NuGet. In my opinion, this is terrible for the future of those languages. The quality has taken a massive nosedive. I never had to worry about the compiler throwing random internal errors with C# before, but I do now. I recently had to try and work out why a transitive dependency was causing dotnet core to fail, and I basically couldn’t work it out after weeks of research. Other people hit the same dead end.
That! That is the crux of the problem! Why is this extra thing necessary? Why doesn’t
crates.io already have this as a built-in feature? Why can’t creates have some sort of “official seal of approval”? Why can’t we determine if it’s safe to include a crate without having to trawl through web pages manually?
How do you know that some 3 year old block of code you pick up and compile hasn’t suddenly been p0wned via some transitive dependency?
Is this the future of Rust? I suspect so…