I'm moving away, here's my two cents

Rust is a great language but I think it has problems. Those problems come from the the time Rust was being developed by Mozilla and more problems were created after it became a community project. This post is a critique to Rust language to help you guys to improve it.

I started using it because I thought it would be a replacement for C because it has no garbage collection and zero-cost abstractions.

What made you quit?

What made me fed up was that I was getting SIGTRAP when I was trying to make a program that supports plugins. If it was SIGSEGV I would know that a statement is corrupting memory but SIGTRAP terminates the program and leaves me with no clue. No message is printed. I know exactly which statement causes the problem. I was trying to copy a struct into another struct. Copying primitives works fine. To be honest I couldn’t even setup gdb to show the source code, I know it is possible though. I don’t have to do any setup when I’m programming in C. I just run gdb and the source code is there.

FFI

Rust seriously does not support interfacing with other libraries. People write entire libraries just to call code from another C libraries. If I’m calling code using the C export interface, I know it is unsafe. No need for wraping. Furthermore, it is C code. Why do you guys depend on C? Call functions using the int instruction in assembly source code.

The language

The syntax has too many tokens. It makes programming unpleasant. A good example of a language with perfect shape is the Go language, very clean. struct fields end in coma, trait fields end in semicolon and statements end in semicolon too. Why not make everything a semicolon? What is the -> for before the return type? Why the semicolons after the identifiers in struct declarations? Rust does not support anonymous structs and they have to be always declared outside of the functions. But functions can be declared inside other functions, what an irony. Closures are something that I never needed. Entirely superfluous. Tuples are completely unnecessary. They are like anonymous structs with anonymous fields. When declaring a new struct instance, the fact that the field name have to be always used is a pain to the programmer. It should be optional as in C.

Packages

There is a lack of interface standardization for packages. Something like RFCs (Request for Comments) and packages that provide just the interfaces for specific applications. For example one interface for each type of database (table, kv, document). The Go language provides a common interface for SQL databases. This makes cargo.io bloated with packages that do the very same thing with different interfaces, work is doubled and human time is wasted. When I visit it I simply have to spend hours to analyze which package does it better and which has the better interface for my program.

There is a lack of modularization. Most libraries I used in Rust depend on many other libraries. It is a design failure. Libraries should have no or very few dependencies and the host program makes them interact.

Why people keep using names related to metals for the packages? Rust is a family of fungi. If people used names related to biology it would make more sense. Something like “microscope”, or “test tube”, or even “bacteria” or “lion”.

Build environment

No one talks about out of tree builds. It is essential for making backups of the development environment. It is even worse than it sounds. Each package builds its dependencies on the target directory so every time I make a backup of multiple projects, the dependencies get compressed not just once but multiple times, once per project. The solution is to clean each project one by one.

Cargo is just yet another package manager and the sources are centralized by default. Why crates.io by default and not github or any other website? Why not use the debian repository and make deb packages if it is not pushed in time to sid. Or maybe produce multiple packages in different formats if you don’t use debian. I can setup my /etc/apt/sources.list to point to tor://vwakviie2ienjx6t.onion/debian/. The fact that crates.io is the default makes Rust less resilient and vulnerable to censorship and surveillance.

The compiler

Too many warnings. It looks like running gcc with -Wall. That’s some flag that I would use after finishing the project. Things like unused variables, unused imports are just things that I don’t want to know when a program is under heavy development. The compiler outputs warning that do not affect the behaviour of the binary like braces around if expression, camel case for struct names and so on. Coding standard should be optional.

The compiler errors are cryptic and contains language not present in the book. For example: error: the trait bound std::io::Read + 'static: std::marker::Sized is not satisfied This could be translated to "you are trying to use a trait instead of a struct. Traits do not have a size in memory." Or this example: error: the trait bound T: std::io::Read is not satisfied [E0277] The generic T does not declare the trait std::io::Read. The terminal is bloated by note messages by default, no option to activate it if I really need more info.

The compiler embeds help messages with rustc --explain instead of putting them in man pages or maybe just copy pasting the error code on a web page.

The documentation

The documentation looks like a literature book. It’s one of those things that you read laying in bed before sleeping. It teaches the language like a person trying to learn it by trial and error and uses onomatopoeia like the author does not know what she is doing. She is just faking feelings and maybe the reader didn’t even understand the matter.

It presents trial and errors and shows the correct way at the end like telling a story. I just want to know the correct version. This causes TL;DR. It presents vague information about the stack, the heap, when memory is allocated and freed, what information a reference, a pointer, an identifier and an array carry. Where my data is stored anyway and how much space it is taking? It does not explain why it is possible to return structs and things wrapped in Result and Option. As far as I know it requires an optimization from LLVM to put the data on the caller’s stack frame instead of putting the data on its own stack frame.

Better information is missing for features unique to rust like ownership and lifetimes, specially lifetimes. It’s so terrible that I had to create a stackoverflow account just to make questions about it when I could just have googled it as usual.

The page about drop contains references to fireworks, bombs and explosions, things that I don’t want to think about when I’m concentrated. A technical book is not place for happiness symbolisms.

Good bye

I was using Rust mostly to make web apps. But now that virtualization is widely available, I can use C and the good old Apache with fcgi. I can now insulate the requests from every user and prevent it from modifying the operating system. I can use a memory management strategy like the one that the Ada (considered super safe) uses: always free the memory on the caller function. I will go back to C and I know that I will get SIGSEGV. It’s like the Socratic paradox says: I know that I create bugs. Rust has a great community. Anything you need you can count on me.

Good bye folks. Maybe I write my own language inspired in Rust.

1 Like

Thank you so much for the detailed feedback! Rust is going to keep improving, smoothing out the kinds of obstacles you’ve run into. I do hope you give it a shot again in the future! :smiley:

6 Likes

[Moderator note: If you have responses to this criticism, please remember to be polite. Adding information is fine, but let’s not start any big fights over opinions, especially since the author has apparently made up their mind already. Don’t feel obliged to respond to every single point.]

7 Likes

Cargo does in fact support out-of-tree builds, using either build.target-dir or CARGO_TARGET_DIR . See http://doc.crates.io/config.html#configuration-keys and http://doc.crates.io/environment-variables.html#environment-variables-cargo-reads . Maybe it could be made easier to find.

The extended error explanations are available online at https://doc.rust-lang.org/error-index.html , and there’s a link to that page from https://doc.rust-lang.org/ .

Filed https://github.com/rust-lang/rust/issues/35101 about the SIGTRAP.

2 Likes

@eefriedman By the way, it was not dropped. It was done automagically by the compiler.

@brson Thanks. I might come back.

I started using Rust some time ago for a few projects. While I encountered some of the problems that you mentioned I was very happy to see how most of them were tackled (or are being tackled) by the community. So I could share part of my experience regarding some topics you mentioned.

For example, I had a totally different experience regarding cargo, rustc and package management in general. I have never had such a pleasant experience with setting up a working environment. Some aspects were tedious at the beginning but as cargo gets new subcommands a lot of boilerplate code becomes straight forward. Dependency management, error messages and incremental compilation are also getting better. Installing in different platforms and with different compilers became really easy when multirust/rustup appeared.

Regarding packages themselves, indeed there is a lot of balcanization going on. But this is typical of a new language. People are experimenting with it, trying to see what is the best way to code certain things, what API are most useful. Winners are already emerging and will hopefully gain more traction. What I would like to see are more Traits in the standard library to help interoperation … but it seems to me that knowing which Traits to include needs testing in the real world.

In relation to the language itself, I found it hard a the beginning (and I still have trouble every now and then). I remember trying to code as many things as possible without explicit lifetimes just to see how far I could go. I would like to see changes there, namely simplifying the syntax for usual, best practice cases. For example, I find it really annoying that trait objects as arguments are not made more explicit or that you cannot return “something that implements SomeTrait”. Or that 'static is actually required in code like static NAME: &'static str = "Steve"; Or that there is no partial borrowing. But again, many of these things are being actually discussed and hopefully improved.

Regarding the documentation. I do agree that a more technical description is needed (a language specification!), but the book is something else and it is good that exists. It serves a different purpose with crafted, runnable examples.

In summary, I think a lot needs to be improved, but things are moving (I think) in the right direction. But in any case, is good to read other peoples experiences.

3 Likes

Many of these concerns either don't ring true to me, or don't make sense.

Rust seriously does not support interfacing with other libraries. People write entire libraries just to call code from another C libraries.

Could you elaborate? Rust has pretty good FFI support, calling C functions is just a matter of declaring the function. I have no idea what you are talking about here.

The language

You're allowed to dislike the syntax, but it's not really a strong criticism that there's "too many tokens". Also, you can declare structs inside functions. They aren't visible outside that function, but shouldn't be surprising.

Closures are something that I never needed. Entirely superfluous.

Closures are a big part of the language. If you never needed them, it makes me wonder what you were doing. Even in my unsafe-packed ramp crate, I use closures at points.

There is a lack of modularization. Most libraries I used in Rust depend on many other libraries. It is a design failure. Libraries should have no or very few dependencies and the host program makes them interact.

Doesn't this statement contradict itself? Each library does one thing, rather than having to re-invent everything from scratch. I wonder what you think modularisation is if you think it should result in monolithic libraries with few dependencies.

To be honest, this set of criticisms reads like a "I don't like these decisions". Which is fine, you're allowed to disagree, but they aren't really "problems". The fact that you cite the existence of closures as unneeded makes me wonder if you tried to program in Rust, or some sort of "safe C" dialect. The only stuff that really seems reasonable to me is the error messages, which are always going to be an issue, there is no such thing as a "good error message", only less-bad ones.

6 Likes

Many of these concerns either don't ring true to me, or don't make sense.

Some were just opinions based on my experience with other programming languages. Not just facts, not THE right way to go.

Could you elaborate? Rust has pretty good FFI support, calling C functions is just a matter of declaring the function. I have no idea what you are talking about here.

It is really easy if you are calling the functions yourself and if you know how to deal with C code. But it is not what is going on. There are libraries for calling C databases, calling C dynamic loading functions, wrapper libraries, everything. The real deal is that you have to have knowledge about how the compiler manages memory to pass data back and fourth to the library and prepare memory structures on the host program, instead of just calling functions. The compiler will try to manage memory automagically to make things easy for you. That process is awfully documented.

You're allowed to dislike the syntax, but it's not really a strong criticism that there's "too many tokens". Also, you can declare structs inside functions. They aren't visible outside that function, but shouldn't be surprising.

You seem to think that a good syntax is a syntax that compiles. Less tokens equals faster code writing, more productivity, less compiler errors. It's not about being beautiful. I checked the structs inside functions and it really works. Maybe I was using a previous compiler version. And, yeah, it is not surprising. But still, structs can't be anonymous and field names are always required.

Closures are a big part of the language. If you never needed them, it makes me wonder what you were doing. Even in my unsafe-packed ramp crate, I use closures at points.

Closures are usually useful in functional languages like Haskell. They are absent in imperative languages. The fact that Rust is multi-paradigm language makes closures useful only if you like them.

Doesn't this statement contradict itself? Each library does one thing, rather than having to re-invent everything from scratch. I wonder what you think modularisation is if you think it should result in monolithic libraries with few dependencies.

You got it wrong. I said that libraries should not depend on each other. The host program makes them interact. They really should be small.

To be honest, this set of criticisms reads like a "I don't like these decisions". Which is fine, you're allowed to disagree, but they aren't really "problems". The fact that you cite the existence of closures as unneeded makes me wonder if you tried to program in Rust, or some sort of "safe C" dialect. The only stuff that really seems reasonable to me is the error messages, which are always going to be an issue, there is no such thing as a "good error message", only less-bad ones.

Decisions are problems if there is a reason. Bad decisions exist. My idea was to replace C, the dominant language today. I had the open mind to try something new, something better. So, yeah, it was an attempt to use a "safe C". I knew that Rust is multi-paradigm I expected closures to be present as well as other features from other paradigms. I never said they should be removed. I just said anonymous functions are absent, something that I use frequently but closures are there. Closures are unneeded, sorry. You can use them though.

Thanks for sharing.

Cargo is really nice. I just think that another package manager was unnecessary. I still prefer apt-get and cygwin/mingw :wink: Writing a small Cargo.toml is as simple as writing a small Makefile.

I also think that things are moving. That's why I'm considering using Rust again in the future.

Speaking of which: Implement `impl Trait` in return type position by anonymization. by eddyb · Pull Request #35091 · rust-lang/rust · GitHub.

3 Likes

Sorry, but not every one is using (Debian) Linux. cargo is the best I've seen so far.

The point of all those wrapper libraries is to put a safe interface on top of the external library. This way one developer can spend the time to understand the intricacies of "pass[ing] data back and fourth to the library", then everybody else can use it and have the compiler worry about correctness. But nothing prevents you from using "raw" FFI bindings if you wish so.

Would you say that C++, C#, Python and Java are functional languages?

1 Like

The point is to use your own operating system's package manager. Even if it is Windows. You could use Visual Studio instead of cygwin but I'm not sure if it can do non-Microsoft things.

I would say that C#, Python and Java are multi-paradigm including functional (wikipedia it) and closures are new to C++ 11, which makes it kind of functional. :slight_smile: Java is not pure imperative. It is know to be mostly object-oriented.

That makes things really complicated, because I have to package for every package manager out there and I can't use my build config for every OS.

Actually you are doing it with Cargo and you don't even know. When you run Cargo on a platform, Cargo itself was built for that platform. When you call Cargo, it will make a binary to the same platform. This is called a native build. You are using the same config file but you are running a different Cargo on each platform you build. The package is just a file. A zip file for example, but it contains more information such as signatures to verify if the package was not tempered. Creating such package is as easy as calling the package manager of the platform you are using.

Now if you wanted to create different packages using the same platform, you could use the same config file but you would have to use a cross compiler. But it is a totally different story and is just a pain.

Well that's the point: Cargo is doing this for me. I don't have to do this myself. I can concentrate on coding. I created once a deb packages and no: It wasn't easy.

Fair enough. It's easy to build Rust code. Just keep in mind that Cargo is language-centric instead of operating system-centric. So if you need anything from the operating system like having a library installed, a server, a daemon, you will have to install it manually. I'm not sure to which level Cargo is capable of building packages from other languages. This is a burden that requires man work - the maintainers. Furthermore, Cargo does not seem to distribute binaries, just source.

I thought the same when reading the post. I will not go and argue why I think @mateus is mistaken about this and that, because it's ultimately irrelevant. @mateus is part of the target audience, looked at the resources offered, and came out with the opinions and judgements described at the start of this thread. If these opinions and judgements are surprising or arguably incorrect (insofar this predicate can even be applied), then IMHO the best outcome is not arguing against them but to treat it as a case study for why someone might be disappointed in Rust. This certainly does not mean that Rust needs to be changed to conform to exactly what @mateus expected from it, but:

  • Some of the things mentioned are known pain points. These are not the most interesting ones here, because we've all heard them a couple times already, but that makes them no less important.
  • Some things can be traced to a lack of documentation (as other answers have pointed out). Again, not super specific to this post but still important.
  • Another aspect is "not using language features". To some degree this is personal style, the rest of it might be willful avoidance of the functional programming paradigm. Aside from being a sobering reminder that some portion of the programmer population is not sold on FP, this suggests to me that there's space for a semi-introductory text that discusses program design (at small scale). Specifically, to advertises the various paradigms embodied in Rust and how to choose between them and combine them. (This idea isn't solely because of this thread, but it adds another angle to it, the angle of "I don't even like this paradigm why should I use it".)
  • Some issues are related to the ecosystem, tying into the current "Rust Platform" discussion.
  • Some things are really just opinions, on contentious and over-discussed topics such as syntax design philosophy. Not much that can be done about that, but a sobering reminder that there are other opinions is probably good for humility :slight_smile:

Finally, I am not quite sure if I understand your issue with FFI, @mateus. Are you complaining about the safe wrapper libraries that exist and "hide" the down-and-dirty FFI work from its users? Or is it anything in the core language that could be improved? I'm asking because one criticism I recall hearing from time to time is that Rust's FFI is not quite as smooth as possible because one has to deal with the ABI and other issues vs. just including a header and being done.

9 Likes