Rust Objectives Observation


#1

Apologies in advance if this sounds like an ignorant criticism, coming from a newbie.
However it may sound, this is meant constructively.

C, despite its (safety) failings, had gained wide usage chiefly because of its efficiency and simplicity.
Note the three important objectives: safety, efficiency, simplicity.
Rust wins big by managing to unite the first two but is not so hot on the third.

I have in my time written programs in all kinds of weird and wonderful languages.
Unfortunately, most of them have fallen by the wayside and are rarely used today,
mostly because they were considered too strange or complicated by potential adopters.

Why am I even saying the obvious? In the hope that Rustaceans will take every opportunity
to seek simplification. It is not easy and it does not happen without conscious efforts
in that direction.


#2

Note that in general, there is a compromise between simplicity and expressive power. Simplicity makes programming languages easier to learn, but expressive power makes it easier to write and maintain large and complex programs.

This means that programming languages which want to be used for writing large-scale systems must always walk a very fine line between something which is so complex as to discourage potential learners (think e.g. C++, C#, Scala, Haskell), and something which lacks the necessary expressive power to make large programs convenient to write (think e.g. C, Go, BASIC, FORTRAN).

Of course, it is also possible, and indeed very easy, to create languages which are quite hard to learn without bringing any special expressive power in return. An obvious example would be Assembly: it is quite hard for humans to learn and use, yet provides no special abstraction to ease the development of complex systems. C++ is arguably a bit guilty of this as well, due to its enormous amount of unnecessary legacy baggage and “tricky bits”. I would refer to such “gratuitous” complexity as accidental complexity or complication.

Another less common possibility is to build something which is internally incredibly complex, yet manages to be easy to learn through good learning material and extreme effort towards hiding the complex bits from the eye of the new learner. Python is a good example here: I will laugh at anyone who tells me that it is a simple language, yet it is certainly something that people can pick up very quickly.

In my view, Rust has managed quite well to avoid accidental complexity so far, noticeably by focusing on a relatively small number of abstractions (pattern matching, type inference, traits, move semantics and borrowing, iterators…), and perfecting them so that they can be used on a broad range of circumstances.

But there are indeed a couple of areas where usability could be improved. For example, lifetime annotations are currently a major usability pain point of the language (just look at the amount of requests for help on this matter in this forum), and would definitely be a high-priority area for future usability improvements.

Smaller ones exist, some of which have been discussed on the Rust blog. One example is the interaction between pattern matching and references. Who never got confused by use of “ref” and “&” in pattern matching? Another which was also discussed on the Rust blog is the “use”/“mod”/“extern crate” dichotomy.


#3

One of the biggest things on the 2017 roadmap is learn-ability and ergonomics: https://blog.rust-lang.org/2017/02/06/roadmap.html


#4

I wanted to point out here that C isn’t actually a simple language. I think a more apt term here is “small”. The C standard is in fact relatively small, but this actually leads to one of the biggest failures of C: the huge amount of undefined behavior. I designed and taught an introductory C course and it is actually very difficult teaching many concepts of C because there’s almost always an exception or edge case. I think that C is the English of languages, full of corner cases that you might not know unless you’re immersed or a native speaker.

As @steveklabnik already mentioned, improving Rust’s ability to be learned is on the agenda for this calendar year.


#5

As a new Rust programmer I think Rust is hard because it forces you to think about things you didn’t have to think about previously, namely where your data lives in memory, and how you’re data is getting changed in your program. It’s the forerunner for what’s to come in language development, and pulling things along into the next generation is never easy. For example, people are still coming to grips with async programming models in Javascript and that’s been around for at least 10 years. Figuring out ergonomics comes with experience and feedback from lots of different projects, and that takes time.


#6

To be constructive you need to spell out where you see failings in simplicity.

Many complications come from safety and efficiency taking priority. Most concepts in rust are not completely new to seasoned programmers; just adaptations of past language constructs.


#7

Rust has more parts the C, but in C programs you often need to handle the same complexity you see in Rust code. But in C you don’t have a compiler that helps you avoid many traps and helps you design abstractions with less work. So I agree C is smaller but not simpler. If I want to write complex efficient correct programs Rust is actually simpler than C for me.


#8

There’s a relevant talk by Scott Meyers about why C++ is successful despite being so complex: https://www.youtube.com/watch?v=ltCgzYcpFUI.


#9

I’ve also felt this way before, but after going through the process of learning many other languages since then, I’ve come to appreciate the relative design purity of C, which I would categorize as a form of simplicity.

Sure, the syntax is terrible. C-style for loops, the comma operator, assignment returning a result, delimiter-less if/else/for/while… these are all awful. Don’t even get me started on that. C was created in the 70s, and we certainly had time to learn from our mistakes since then.

Sure, it is full of pitfalls. Arrays as pointers were a terrible idea in hindsight, pointer arithmetic should not have become a standard tool, and aliasing by default was the single most performance-killing decision ever taken when designing a programming language, in the face of which properly implemented garbage collection is a second-order effect. And yes, there’s all the undefined behaviour too. And the biggest, imo, is manual resource management. Again, we’ve done better since then.

For all these reasons, I would not recommend learning C as a new language without being forced to by external circumstances, or building new programs in C. It’s legacy to me. It has been outperformed in every single of its application domains, and since everything else is ABI-compatible with it, even the fact that a gigantic heap of existing code is written in it is really not a very good excuse.

But when was the last time you saw a language doing so much with integers, floats, null-terminated arrays of bytes, data structures, functions, pointers, and a very stupid preprocessor? To me, this is the incredible thing about C. That its community managed to pick a small, extremely focused set of features, and be creative enough with them that they could build very complex things such as operating systems, GUI toolkits, 3D engines…

I really think there’s a lesson here to be learned for new programming languages, about resisting to the lure of new features before you’ve really taken the time to understand them and you’re sure that you really, positively need them. Better be general, even if it increases the learning complexity, because people only learn the language once and read code written in it many times.


#10

I disagree. It’s one factor but I’m not even sure it’s correlated. Perl is pretty expressive and many would not consider it to be maintainable for large complex programs. Expressive power is good for one liners.

I contend that simplicity and regularity are needed for maintaining large and complex programs. And when they expose a composable interface that permits expressive one liners then you have a winner. In this sense, Rust seems to have pretty good citizens. But I agree with @rustafarian that it’s overly complex for new developers.


#11

Assuming this isn’t a rhetorical question: Forth. The 8086 became self-hosting with a Forth system before any C compilers ran on it (because Forth works so well for interactive use, there are systems where the entire OS is just a Forth REPL).

Nowadays, C has kind of replaced Forth as the preferred programming language when you want a minimal runtime. For lack of a better explanation, I chalk it up to Forth being even harder to run compiler optimizations on than C.


#12

Yes, can you give some examples of things you consider to be complex and how you would simplify them, @Rustafarian?


#13

…and for pretty much everything that looks like mildly advanced math (complex numbers, linear algebra, calculus…) or a general algorithmic pattern (iteration, sorting, automatic computation parallelization based on data or task parallelism, serialization, parsing, AoS/SoA transforms…).

When the programming language that one is using lacks the required expressive power for the problem at hand, one ends up either writing almost the same code over and over again, or using extremely fragile code generation strategies to automate that repetitive work. Such practice lead, in turn, to a more fragile codebase. Hence my claim that expressive power is important for large codebases that do complex things.

You can afford to repeat yourself once or twice in a 100-lines programming course assignment. You cannot afford to do so thousands of times in a library weighting millions of lines of code. When you reach this scale, you need to either use a language suited for the problem at hand, or adapt the one you’re using so that it becomes less painful to cope with.

[quote]I contend that simplicity and regularity are needed for maintaining large and complex programs. And when they expose a composable interface that permits expressive one liners then you have a winner. In this sense, Rust seems to have pretty good citizens. But I agree with @rustafarian that it’s overly complex for new developers.
[/quote]
Indeed, simplicity also has its advantages in large programs. For example, it means that someone reading the program has less things to think about, and thus can more easily spot suspicious patterns in it. Combining this and the above concerns, I think there is a lot of reason in a design philosophy which states that everything should be as simple as possible, but no simpler.


#14

For the first objective of the Rust’s 2017 roadmap: Rust should have a lower learning curve. Well, I think if the usability of lifetime annotation can be improved as @HadrienG said, which will effectively lower the learning curve and make the Rust easy to use.


#15

@jonh @carols10cents[quote=“carols10cents, post:12, topic:10348”]
Yes, can you give some examples of things you consider to be complex and how you would simplify them, @Rustafarian?
[/quote]

Fair point.

Take, for example, the two different syntaxes for restricting generic types.
If you argue, with some justification, that “where” clauses are more general and more readable,
then why not have just those?

One thing I find most confusing and superfluous is when there are several ways of writing the same thing.


#16

Fair point, two ways to do one thing.

If I remember my rust history (rustory?) correctly, that’s one due to “historical reasons”. We started out with only the : syntax, which quickly became unreadable for more complex type bounds or functions taking multiple generic arguments.
Where was added to improve this, but removing the old : syntax would have been a very breaking change.

It would also lose consistency with let declarations, which only accept the : syntax
(Don’t​ they?)

The balance seems to have ended up that “simple” bounds get a :, and that we switch to where whenever it becomes “too unreadable” (which is, admittedly, subjective)


#17

A historical note:

Expressivity was explicitly noted as a non-goal in rust’s original design (it says so in as many words in the manual). I believe overzealous pursuit of expressivity frequently detracts from comprehension, thus safety. As with all balancing acts, too little expressivity produces a different form of comprehension risk (through verbosity) but I generally wanted to keep the design on the simple-predictable side of the line.

You’ll note the language didn’t have closures at first, much less type classes! This was because I literally think they are too expressive for their own good – too much magic is implied by writing one – which detracts from comprehensibility.

This design preference has largely been overruled by a sufficient set of people showing up with backgrounds in Haskell and C++, both of which have much higher complexity preferences (at the cost of comprehensibility and, I feel, safety).


#18

(To be clear, I think Rust has continued to pay attention to cognitive load more than many languages, and it’s quite likely my preferences were and are unrealistically conservative. A lot of early Rust code was repetitive and clunky. I didn’t mind that clunkiness, but it grated on many people, and it’s likely that Rust would never have become popular without the level of expressivity it eventually settled on. I know the core team takes this tradeoff seriously too.)


#19

Unfortunately there weren’t enough Ada programmers showing up :slight_smile:


#20

Unfortunately there weren’t enough Ada programmers showing up :slight_smile:

Heh, that is exactly the population I half-hoped / half-expected to be the countervailing force :blush: