Ask HN: Why aren't modern programming languages, like Rust, more legible?

62 points by misswaterfairy ↗ HN
This is a question I've often wondered, and am perhaps asking now out of sheer frustration. I have ADHD, of which my particular 'mutation' of ADHD makes it difficult to read and absorb written text.

I'm not afraid of programming in general, being a data analyst by trade I often use Python and SQL in my day job. I've trained on Java, and C# (though admittedly with some difficulty) and had dabbled in Delphi (Object Pascal) and Visual Basic as a little girl so the concept of programming isn't new to me. Perhaps the 'low-level-nes' of Rust, a language I would love to learn, is new to me which is perhaps one of the reasons I'm having some trouble understanding it.

I'm struggling with heavy implementations of symbols, and perhaps 'shorthand' reserved keywords in languages like Rust, C++ and to some extent JavaScript; I find they aren't as readable as 'higher level' languages like Python.

Given that Rust is one of the newer languages on the block, Python is 21 years it's senior, was the syntax of Rust determined by performance factors or because of it's 'low-level-ness'? Are languages like Python and SQL more legible because they're interpreted and compiled into bytecode, rather than just compiled into specific machine code? Or is it purely a decision of the language's developer?

This question is focused on Rust specifically, given that's a newer language, perhaps wondering why it didn't adopt some of the design principles of easier to read languages, though I am certainly not singling it out. Are there ADHD friendly tutorials/books on Rust?

78 comments

[ 1.7 ms ] story [ 137 ms ] thread
Syntax is a decision of the language's developers.

There is a tradition of using symbols in languages of the C family. Rust is in that space because it addresses the same kind of problems that C solves. Familiar syntax helps developers to switch language.

I guess that a Pascal like syntax for Rust is possible. Would C developers have liked it more or less than a C like one? My take is: less.

An example of a compiled language with little use of symbols: COBOL.

Rust is also descended from ML, not just C, so it had two symbol-heavy lineages to draw from.
Low-level languages like Rust and C++ tend to have more complex syntax and symbols because they provide finer-grained control over memory management and what not, while high-level languages like Python prioritize readability and ease of use, sacrificing some low-level control in the process.

It's worth mentioning that Python, for example, can still achieve good performance through the use of optimized libraries and just-in-time compilation techniques.

> Low-level languages like Rust and C++ tend to have more complex syntax and symbols because they provide finer-grained control over memory management and what not

Modern Pascal provides basically as fine-grained control over memory management as C, while not needing "complex syntax and symbols", really.

As someone also with ADHD I agree. I can and have programmed in C++ and Rust but I very much dislike the noise and clatter. I don't believe it's necessary given the rise of LSPs and the ability to query relevant information as needed.

It's also a big factor of why I enjoy programming in Nim, which provides low level performance but with minimal use of sigils. Though I also appreciated Julia's syntax as well that also provides good access to performance.

It's not modern languages, it's Rust specifically - they didn't optimize for readability and ended up with a language that's hard to read.
Not really.

Rust is plain hard and complex. There is only so much you can simplify that.

Like, you can make jet fighter easier by adding ways to automatically make some things so pilot doesn't have to do all the steps (like say Rust does with some of the memory management), but it will never be as easy to fly as it is to drive a bicycle, still need to learn how everything works and what does what

Programming simple things in Rust is less readable than programming the same things in other languages - and you can directly point at the language design as the root cause for this.

I'm here doing things like creating an object, iterating over some structure, building a simple function, reading or saving some data, making a small REST API - and while I'm doing these things I have to work with complex syntax and language semantics because the same language also caters to use cases that have nothing to do with the thing I'm working on.

Gonna have to hard disagree here. Rust developers straight up opted for obscure, weird, idiosyncratic, non-self-explanitory syntax.

'?' doesn't mean anything like what it means in every other programming language, and it's everywhere. It is so frequently used that you get used to it pretty quick, but it is very not obvious to a beginner.

Obscure #[blah(blah)] attributes attached to nearly every line of code in some places. Do those have meaning? Are they just hints to the compiler? You'd better hit the docs to find out.

Do I need a &mut Option<Thing>, or an Option<&mut Thing>? And how do I get that out, as a `opt.as_ref().mut()`, `opt.as_ref_mut()`, or &mut *opt`? Are these valid? What's the difference? Idk, it's inscrutable.

Rust syntax really, really sucks my friend.

> Do I need a &mut Option<Thing>, or an Option<&mut Thing>?

Follow up to this example: let's say that I don't know the answer, what do I search for if I want to find out?

Man, I don’t even know. It’s ungoogleable.

The only thing that keeps this from being as big an issue as it ought to be is that rustc’s error output is really, really good. Usually it tells you how to fix it.

It's more of an issue with rust concepts, not syntax. I had the same issue when I wrote lisp the first time (and it ended up being a pretty complex finite state machine).

The second part of your question is bad syntax however, I agree (but still, 90% of the unscrutability of rust is caused by its concepts imho).

(comment deleted)
> Do I need a &mut Option<Thing>, or an Option<&mut Thing>?

That depends on what you want. Do you want the address to a mutable Option of a Thing, or do you want an Option of an address of a mutable Thing? The mental model you have of your code should make this rather clear.

I think you just don't need Rust. Your comment suggests that you didn't even try to learn the language or maybe hoped that you'll master it just by looking at it?

It's a complex language with unique properties, and it's syntax was carefully crafted towards it's goals - high performance, close to hardware, no runtime, memory safety etc.

I think it's just a personal preference of the creators
Asking out of genuine curiosity: What defines an ADHD friendly tutorial? Might be easier for non-ADHD developers to help find what you are looking for if we know what to look for.
> What defines an ADHD friendly tutorial?

I have ADHD. I also wonder about this.

I take my time digesting the content. Mainly this is because I process text slower than average. I always prefer short sentences. For long sentences, I split them up and interpret each part.

Books for kids are usually written with short sentences. Maybe tutorials "written for kids" might be an answer? But that sounds condescending. So maybe it is not the answer?

Finally I will add that I firmly believe this. It is more about "how you consume the content" and less about "how the content is made for you".

I have ADD. The best tutorials for me are code heavy. Show don't tell. A chunk of code to illustrate a point is a million times better than a couple of paragraphs of text.
This is a conscience choice that has been made by the Rust team - they've chosen to create a very "powerful" language (which makes sense if they are trying to compete with C++) and thus require a very powerful syntax, at the cost of clarity and simplicity. I'm with you personally, it reads as a mess to me.

The opposite end of the spectrum is Go, which I love, but many hate for being too limited. I like seeing everything in the control flow represented as values - context and errors included - but too each their own. I'm not sure if you need to learn Rust for work or are just interested in a new language, if it is the latter I can recommend checking Go out. :)

The problem isn't the syntax, it's the semantics. Matklad has a great post about this. You can try a bunch of different syntaxes, but rust doesn't get more readable with any of them. You need to remove information (and thereby change how the langauge works) for readability to improve.

And personal preference here, Rust is, with a few exceptions, actually one of the nicer languages to read once you add/remove all the extra information that rust requires.

I find all examples in that article more readable, outside the c++ one.

(But I am personally not bothered by Rust’s syntax)

Just wanted to mention Nim here. Others mention Go but it has a lot of "noise" because of the error handling and other features. For native binary creating languages, I haven't seen any language "neater" than Nim although I hardly go out of my way to sample languages.
Go’s error handling is not noise, it’s signal.
It's signal in a sea of noise. The premise (errors as values) is done better and more concisely by languages like Rust, Haskell and Ocaml with ADTs.
> Go’s error handling is not noise, it’s signal.

It's a ridiculously noisy signal, which makes it difficult to distinguish from noise: because all the error handling looks the same and is extremely verbose, it's difficult to distinguish special cases you need to pay attention to.

Tons of languages have "errors as values", Go stands out in its verbosity, as well as its lack of safety / certainty.

Of course Results/Options would have been better from the start, but it’s too late for that.

As a dev though I infinitely prefer Go’s error handling than exceptions for instance, specifically because of the verbosity and explicitness

Yes, as a beginner, I was really surprised how easy to read Nim is. I've been learning it as my first programming language (apart from bash, lua, and unsuccesful attempts to learn Python) for a couple months now. And the fact that I could just open source code for standard or external libraries and immediately understand what it does was immensely helpful.
ADHD doesn’t affect your ability to read. You probably have dyslexia.
[flagged]
There is absolutely no need to name call like that.
Oh i thought we were just throwing out assumptions. My bad.
Well if we assume someone is doctor or moron with no state in-between like you suggest, how would moron like you notice the difference ?
Like the "in-between" of ADHD and dyslexia? I'm being facetious to make a point.
Being skeptical of the sudden rise of "everything is ADHD" when the end game is access to stimulants following years of relaxed regulation on prescribing those drugs is perfectly reasonable.

It's amazing how every symptom became ADHD when unscrupulous VC-backed startups took the pandemic as an opportunity to start marketing and prescribing ADHD medication online.

I feel that's a separate topic though and I won't assume that the op for example is misdiagnosed. I can only take what is asked at face value really.

I will say though it's interesting how in the US ADHD seems to be as common as hay fever whereas in the EU seems to me that it's very rare to be prescribed for ADHD.

FWIW, dyslexia and/or dysgraphia are common ADHD comorbidities.
Yes, because all mental illnesses fit into perfectly neat boxes.
No one is talking about mental illness here. ADHD and dyslexia are not mental illnesses.
I don't have any significant experience about ADHD or Dyslexia, either in professional capacity or as a layman to make diagnostic statements.

However, just going by definition of Attention Deficit Hyperactivity Disorder, continued focus is an issue. When you have code like below where a bunch of attention/focus might be needed to just to parse it/keep it all in your head and those things are biologically in deficit, I can imagine it won't be smooth sailing:

    trait Processor<'a, 'b, T>: 'a + 'b {
        type Output: 'a + Clone + Debug;
        fn process<U, V>(&'a self, item: &'b T, other: U) -> V
        where
            U: 'a + Clone + Debug,
            V: 'a + Into<Self::Output>;
    }
PS: for rustaceans, this is made-up code to make a point so please don't try to optimize it.
> ADHD doesn’t affect your ability to read

That’s a very strong negative claim that deserves citation.

Wouldn't ADHD medication make it easier to focus on one piece at a time?
The question you say is focused on Rust, but I will answer for the (baity?) title.

Why are moden programming languages, like Go, so plain?

> I have ADHD, of which my particular 'mutation' of ADHD makes it difficult to read and absorb written text.

Don't think that's ADHD bud.

OP's not talking about legibility, rather context-span and information density.
I’m not sure who made you the is-ADHD police but yeah it can be bud.
ADHD can make it hard to sustain attention while reading. I tend to also zone out when reading something even slightly uninteresting or in this case something (eg a Rust program) too dense.
I'm a polyglot, I've used many languages over the years.

I have less of a problem with Rust than Swift for example, because Swift is really hard to decipher at times. You can leave out chunks of code and it's supposed to be obvious what it does but it's not. The whole language can become dialed down to looking an like a DSL; for example SwiftUI. It's confusing as hell, e.g. Button(action:{}, label:{}), Button{} {stuff} etc.

Rust is certainly a mouthful to look at, but it looks like Rust. Python's syntax is a big part of python's success. However I find that personally, I don't like meaning through indentation. I prefer the safety of `;` and `{}`.

My advice is spend more time writing Rust, eventually your brain becomes selective at reading things that matter and ignore things that do less, in context of what you're looking for, whilst reading.

Leaving aside the question whether Python is actually readable outside of small scripts:

An untyped language or a language with a small type system and type inference like SML always looks more readable.

If things get more advanced, the syntax will grow. Now, whether Rust needs to look that ugly is another matter. I think they made some wrong choices and should have evolved more from SML etc.

I think legibility in programming is mostly familiarity.

Whether it's a C, a Lisp, a ML, or an APL — spend enough time getting your hands dirty with it and you just get used to it.

I never had to get used to Lisp, Python, Nim, Julia or SmallTalk syntax. It was easy and natural from day one. Getting used to C-style syntax (C, C++, Java, JavaScript, Rust, etc.) took some effort, and I have to work to get used to it again every time I pick up a C-style syntax language again after working for some time in a different language. I can’t put my finger on exactly what it is, but there is something about that particular syntax that my brain just refuses to internalise.
I am a beginner to programming language implementation. I have a JIT compiler that compiles a language that looks similar to Javascript.

My favourite languages are Python, Java and C.

I have written 2 Rust programs. I am not sure I understand C++ "forward" and Rust's lifetimes with the apostrophe. Any explanations would be helpful.

Async Rust, traits all seem hard to understand to me. I would like to understand them better.

I like C programming because it feels simple.

With Rust it feels that everything is a fight, I haven't yet got the mental model to design Rust programs. I think I need to design programs as trees.

I can deal with * symbols and &.

I find into(), forward hard to understand.

I find C++ template syntax hard to understand, things like unique_pointer.

Do you mean C++ forward declarations? If so, it’s basically an entry in the linker table. A placeholder of sorts. You can’t reference something without it being known, so you have to make it known first.

Where C++ gets weird is that you can’t instance something without it being fully known (the full definition needs to be visible). This is rooted in the .cpp / .h header split, which as far as I know is pretty unique to C.

std::forward (and reference collapsing rules, which are related) is imo one of the most complicated parts of C++. Generally, the idea is that it's there to implement "perfect forwarding". Essentially, being able to accept and pass to another function any type of parameter without coercing it to something like a const reference, which can be inefficient, or limit the way a caller can use your function.

I'm not trying to gate keep, but if you don't understand unique pointers and templates in general, you're a long way from needing or being able to make use of perfect forwarding imo. You don't need perfect forwarding outside of generic wrapper type code, and in most code you can just accept a specific type of object (e.g., value types or const references).

If you want to understand it you should start with learning value categories, move semantics, and templates, and then try to learn perfect forwarding.

The bit between the < and > I find hard to understand.

In C++ behaviours are named, like in some C++ code coroutines there is a template that looks like:

template <is_task task_t>

The is_task looks like an if statement.

My impression is that coroutines aren't super stable today, but what you're seeing there is a C++20 "concept", essentially, a way to restrict the types accepted by a template. E.g., the is_task part is a concept that requires the template parameter type task_t to meet some criteria.

Normal template code looks like:

template <typename T>

Where you can also choose to use the class keyword instead of typename.

C++20 isn't very widely used though, so this isn't something you're likely to run into in anything other than cutting edge code. If you want you can read about them here: https://en.cppreference.com/w/cpp/language/constraints but you're probably better not using concepts at least at first. In general, https://en.cppreference.com/ is a very high quality source for learning, although it's very dry as well.

> Are there ADHD friendly tutorials/books on Rust?

Work your way through the Rust Book, but do it slowly. It is all online. It is what I am doing right now. Here is a link for you. https://doc.rust-lang.org/book/

I don't think this is related to your ADHD.

When your read a program it puts cognitive load on your brain. How much is a bit depended on your education and experience on the subject, but what you have to do is to transfer what you are reading to your mental model what the program is actually doing. Your capacity of things you can process in your brain is limited so you can fairly assume that the more load is put on your brain the more of that capacity is used, and the nearer your are to your maximum capacity the more difficult and exhausting is the task to understand the program.

When you need to decode a "{" or a "@" in the program to a actual processing step. you can assume that

  if (a) {
    b;
  } 
  else  {
    c;
  }
is more difficult to decode compared to

  IF    a 
  THEN  b 
  ELSE  c 
  ENDIF.
simply the latter one is nearer to your natural language (I actually think to replace "ELSE" with "OTHERWISE" would be even better) and the "ELSE" implies both the opening and the closing braces.

You can argue if

  a ? b : c; 
is more readable, I would argue it is not.

It's not like special characters are always bad. For example

  a->b
works quite well when a is pointer and the arrow "->" symbolizes graphically where a is actually pointing at. But nowadays we languages which use "->" and "=>" for different things and for their similarity they interfere which each other when doing the decoding and adding to the cognitive load.

When designing a language you have to trade of brevity or number of characters to type and readability. Most, if not all languages, nowadays opt for brevity because it appeals to the developers to type less and makes it more easy have more complex functionality in one statement. The price you pay is readability and finally maintainability of the program.

I think the first programming language which failed by its overuse of badly readable syntax was Perl, which was replaced by the much more easy to read Python.

I personally believe the psychology of programming is an under-researched subject

Disclosure: Alongside to CS I studied cognitive psychology when I attended university.

> simply the latter one is nearer to your natural language (I actually think to replace "ELSE" with "OTHERWISE" would be even better) and the "ELSE" implies both the opening and the closing braces.

Are there really studies that backup this claim? I _feel_ that I process programs and natural language very differently, that I don't look for the same things at all. I don't have a need to "read" the program as if it was text. I don't look for a natural grammar structure in it. And English is not my primary language, and it's not for a lot of people. But then, I've never studied cognitive psychology, and I cannot really say I trust what I think I know about how I process programs.

When I started out programming, it was Basic followed quickly by Turbo Pascal. I tried looking at C and C++ a bit later, but for years I found them hard to read, with the natural language keywords of Pascal feeling much more, well, natural.

As I grew more experienced though, this has shifted. I took some time to really learn C and C++, and while I still use Pascal in my day job (Delphi), I now feel the natural language keywords "get in my way" a bit, and I prefer the brevity of curly braces and such.

For me, this isn't about typing, I don't think that's a very interesting metric and I spend far more time thinking about my code than writing it regardless of language. The milliseconds saved typing "{" vs "begin" is irrelevant to me.

For me it's about parsing. Once I trained my brain on the curly braces, the begin/end pairs take so much space so to speak, they kinda obscure what's really interesting.

But before that, the natural language keywords helped me because my eyes and brain were trained to recognize them already.

That said, I've looked at Rust code and yeah, I'm having that initial C/C++ vibe again, where it all looks like a mess of "random" symbols thrown together. I'd have to spend some serious time learning Rust for that to look acceptable I think.

Thank you. You have described perfectly something I’ve only ever realised subconsciously. This makes so much sense.
This is not ADHD, this is Dyslexia
I think I'm in the other side of your problem? I found easier to read Rust code than untyped Python.

My big issue with python (and others like plain JS) is they don't type things and it's easy to forget what is what

Also Rust program are usually small, in the other hand in Python I see objects that extends from 6 objects and they can be as big as 1k lines with data inside

If you really have ADHD after 10mins reading untyped python how can you remember if you are passing a dict or an object? or what it was 200 lines above of the same object ? or what it has the 6 other objects that you are extending from?

The answer to your last question is that you can't.

Even with types the multiple inheritance example you describe is incredibly frustrating and near impossible to grok.

I love huggingface's transformers library and use it every day, but trying to find out what the fuck is actually going on when I 'call' a model instance (yes, they overloaded __call__) is a kafkaesque journey through the inheritance tree. I must have tried like 5+ different times and I still don't know what's going on.

edit: sorry for the rant, it's just an example of multiple inheritance and python pain.

I find languages with symbols far easier to read than verbose textual syntax like in BASIC or pascal. Symbols like (), {}, [] satisfy my need for symmetry and are far easier to correlate than BEGIN/END. I find Python very unreadable due to the whitespace/alignment thingy, I always get the urge to hold a ruler to my screen to see if somebody forgot a space somewhere.

That said, Rust is okay because it uses symbols, but not okay because it uses them in very weird ways. Macro function stuff that needs a ! like whatever!() is aggravating. The exclamation mark is easy to overlook, easy to mix up with letters, isn't surrounded by spaces and frankly I do not see the point. That syntax doesn't really add anything to the safety of the language that couldn't just be done with a few 'you are overriding stuff' warnings. Actually it makes things worse, because if somebody has stuff() and stuff!() defined, there is hard-to-distinguish weirdness going on. Also aggravating but not as dangerous are 'labels. Oh, and angle brackets like <'stuff> that could easily also be comparison operators somwhere else. Rust would have had the opportunity to fix the mistakes in C++, but it just copied them.

Oh, and all the 'mut' everywhere is also weird noise that would be easier if left out. Most things are 'mut', so write out 'const' instead.

I agree with most of what you said, but I disagree about mut.

Mut makes the unsafe case stand out. It also requires less typing to have the safe case.

Think of it like this. When you hire a team to remodel your kitchen, you can just write your agreement as "do the kitchen, get X money". Or you could go with a over-complicated, lawyered up contract for 10 pages. Obviously, the former is more legible than the latter.

However, the former also leaves so many details unclear. Like, how long should it last? Who provides the design? How do you inspect the end result? How much should you pay up front, and how much afterwards? Who buys the materials, who pays for them? What happens if they accidentally burn your whole house down? And a hundred others.

Python and Rust are like that. Python is very legible because it doesn't specify a lot of things. What is the type of that variable? Is it declared or not? Both of these are fixed with modern linters and type hints, but type hints already make the language less "legible". Is the code that I'm calling able to modify the data I pass in? How exactly are the objects allocated in memory?

Once again, many of these concerns can be unimportant for small projects, just as simple verbal agreements are enough sometimes. But for huge software systems, especially ones where memory layout and performance are critical, all these details are crucial — just as a good contract is.

Legibility is also based on what you're used to. E.g. I find nested parentheses horrible to read, but Lispers swear by them.

You could get a decent IDE to fill in the gaps for you. Otherwise make lots of snippets and save yourself the mental overhead while you get started on making things.