Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> This is a superficial complaint, but I found Rust syntax to be dense, heavy, and difficult to read.

I'm not sure this is a superficial complaint. People say the hard thing about learning Rust is the new concepts, but I haven't found that to be true at all. The concepts are easy, but the combinatorial explosion of syntax that supports them is untenable.



Back when I wrote C and C++ for a living I'd occasionally meet someone who thought their ability to employ the spiral rule or parse a particularly dense template construct meant they were a genius. I get the same vibe from certain other groups in this industry, most recently from functional programmers and Rust afficionados, for example. Nobody gives a damn if you can narrate a C spiral or a functional-like Rust idiom.

And this syntax density is one of the reasons I stopped advocating for the use of Rust in our systems. First, I don't want to work with languages that attract this kind of person. Second, I don't want to work with languages that require a relatively heavy cognitive load on simply reading the lines of the source code. Units of code (i.e. statements, functions, structures and modules) are already a cognitive load--and the more important one. Any extra bit I have to supply to simply parsing the symbols is a distraction.

"You get used to it," "with practice it fades to the background," etc. are responses I've seen in these comments, and more generally when this issue comes up. They're inaccurate at best, and often simply another way the above mentioned "geniuses" manifest that particular personality flaw. No, thank you. I'll pass.


> I don't want to work with languages that attract this kind of person

I haven't used Rust professionally, but I find the community extremely inclusive and helpful. I joined the Discord server and asked all sorts of stupid questions and people always helped me and explained to me what was wrong with my code (or my assumptions). But, again, I haven't used Rust professionally and it may be different in that context

> I don't want to work with languages that require a relatively heavy cognitive load on simply reading the lines of the source code

Strongly agree on this, I haven't tried to introduce it where I work for the same reason. The cognitive load is massive compared to a language like C# or JS and the gain is minimal for the average developer writing microservices for React frontends. In this context you need a JSON serializer, iterators and maybe generics, and Rust is not much better than C# on this front.


It may be my limited experience with C#, but I really missed Rust/serde when deserializing a config file into a class/struct. (Actually INI not JSON.) It feels like doing error handling twice: first catching parser exceptions, and later checking for null fields everywhere. (Basically I was forced to default-construct the object before parsing, which either means nullable fields, or having hardcoded defaults for every field.) I guess the pragmatic thing is to turn off null warnings and accept that it may fail later.


> I get the same vibe from certain other groups in this industry, most recently from functional programmers and Rust afficionados, for example.

Another trait in programmers that is worth avoiding is the false equivalency between C++ template metaprogramming and generic programming in languages with expressive static typing.

It's not clever or inscrutable like templates, quite the opposite. It's explicit about constraint. Generic Rust makes it easier to understand complex code and write it correctly. An immediate red flag for me are programmers who don't "get it" because they equate that to some kind of SFINAE or compile time magic they once saw in C++. They're not the same feature, except superficially.


>Second, I don't want to work with languages that require a relatively heavy cognitive load on simply reading the lines of the source code. Units of code (i.e. statements, functions, structures and modules) are already a cognitive load--and the more important one. Any extra bit I have to supply to simply parsing the symbols is a distraction.

The weird thing about these comments to me (as someone who doesn't use Rust) is that the most difficult syntax in the original examples represents a semantic detail that most languages don't have to deal with: the lifetime. The amount of times I think about the lifetimes of variables I write in Python is zero. Parsing the symbols and understanding the code here aren't separate; that weird apostrophe thing in angle brackets is a symbol I don't use referencing a concept I don't use, which fits. If you replaced the symbols with keywords or something, it would just be longer, not simpler.

Also, it's a choice to write your code like he did. You can define local variables that hold intermediate results and subexpressions and give them descriptive names, if you want. You could assign `drop = (|_| ())` for example.


Yea, so pretty much every programmer needs to be aware of thinking about lifetimes.

The classic issues are creating a local and then giving a reference to it, to something much longer lived, or even undying. In that last case, if your doing it a lot, with no regard to the objects change in lifetime, your effectively creating a leak (I can guarantee you someone, somewhere, is making this mistake in js right now). Most collection strategies won't touch an object that still has a reference to it.

The second issue that became really common when people stopped paying attention to lifetimes, is that many resources you may be using (file handles, db connections, etc...) have very different constraints than memory. So you have to be cognizant of the fact that even though something has gone out of scope, it's lifetime really doesn't end until the gc gets around to collecting it. This is the reason special syntax and functions, like "with" and Dispose had to be added to languages like C# and Java. Python with it's reference counting maybe somewhat less susceptible to this second issue than the others, but it's not immune to it.

Finally in many cases being aware of object lifetimes and specifically manipulating them can get you performance speed ups.


> Back when I wrote C and C++ for a living I'd occasionally meet someone who thought their ability to employ the spiral rule or parse a particularly dense template construct meant they were a genius. I get the same vibe from certain other groups in this industry, most recently from functional programmers and Rust afficionados, for example. Nobody gives a damn if you can narrate a C spiral or a functional-like Rust idiom.

I think one problem is dealing with "just because you can doesn't mean you should". It is easy to be nerd-sniped into optimizing everything in Rust. I've seen complain about an arg parser using dynamic dispatch when anything the program actually does will dwarf the time that that takes. I feel we need a reset; a stdlib-alternative that optimized for those learning and prototyping at the cost of performance. I suspect people using that will help break them of the feeling to optimize the trivial but to instead focus on what profilers tell them.


I’m with you. I think people that treat syntax as some completely unimportant detail are forgetting that reading code is a more important use case than writing code.

No matter how much you internalize the syntax of language X, as the sheer number of syntactic structures in the language increases, the higher the likelihood you’ll misread something.


> I get the same vibe from certain other groups in this industry, most recently from functional programmers and Rust afficionados

Perl one-liner guys used to exemplify this. But I don't really agree that functional programmers do, except for Haskell and people who use lots of the car and cdr compositions, or those who use too much metaprogramming, or... okay maybe you're right. But at least the fundamental premise of functional programming is simple..


Functional programming usually includes Lisp, whose advocates mainly like showing off how powerful and complicated it is. (see: loop macro, call/cc)


I don't use Rust a ton, certainly not enough that the syntax density fades into the background, but something I'll say for the ecosystem is rust-analyzer is really good and pretty much always knows and warns you when you're writing something incorrectly that won't compile. The worst parts of the syntax effectively become self-writing, though it does nothing to help reading.


> And this syntax density is one of the reasons I stopped advocating for the use of Rust in our systems.

Trouble is I've found this type of genius is most languages. There are always some esoteric functionality that few people understand that some people will choose because its "the most appropriate" but largely because its a challenge. Of course such talented people move on to the next project quickly as maintaining their crap is not fun.


If not C++ or Rust, what languages do you advocate for now?


Depends on the application, really. And I wouldn't call it "advocacy" so much as being resigned to accepting a less odious bad option. In that case, typically Go or Python, unless we need that last bit of performance and can't get it with a pre-built library: then I'd argue for C, C++, and Rust (in that order).


Not the OP, I rather use managed languages with AOT/JIT toolchains.

C++ and Rust I leave for scenarios where choice is imposed on me due to platform SDKs, or having any kind of automatic memory management isn't an option.


Rust's memory semantics are definitely a kind of 'automatic memory management' though. I mean, that's the whole premise - to have the kind of guarantees about memory safety that until Rust where only available in GC'ed languages running on some runtime.


There is nothing automatic about compiler errors in lifetimes.

As for until Rust, Cyclone and ATS did it first.


Not sure if Cyclone and ATS don't predate it, but Rust's memory management is a bit like 1/3rd of Linear Lisp (specifically, the compil time garbage optimizer)


To be more precise, Rust uses affine types as basis for the borrow checker.

https://en.m.wikipedia.org/wiki/Substructural_type_system

Its biggest achievement is making them more well known to mainstream devs without CS background.


While I personally agree, I think the bar for readability gets slowly lowered over time. In the Typescript/JavaScript world a for loop used to be (and still is to some) considered more readable than using the functional array functions.

Our reasoning for readability is usually based on what the average programmer is able to read, so the more programmers get used to dense syntax the more readable it is. There's rarely any "real argument" to be had for or against readability.

Haskell and other languages that are inspired by math look very noisy to me, but at the same time I understand that math people don't agree.


> First, I don't want to work with languages that attract this kind of person.

exactly. always noticed people interested in excessive syntax tricks are only good at precisely that. excessive syntax tricks.

zig?


It's not a superficial complaint but it is relative to one's experience. Something that's "difficult" for me might be "easy" for you and vice versa. I find it very much related to understanding the core concepts.

I personally find Rust syntax to be quite enjoyable, or at least it fades into the background quickly - with a few exceptions. The syntax for lifetime annotations can be challenging. And not surprisingly explicit lifetime annotations are a rather unique concept, at least among mainstream languages. IOW the syntax is difficult because it's an entirely new mental model (for me), not because `<'a>` is an inherently bad way to express it.


One human needs to figure out how to write a line of code once, and then that line needs to be read and understood by humans over and over.

Optimize for readability. Rust doesn't seem to do this.


Readability seems to mean different things to different people. You (and many others!) seem to interpret that word as "there's only relevant information and nothing else in sight". Personally I interpret it as "I have all the relevant information available to me in a way I can scan for quickly". Rust has a higher syntactic load, there are more things present to the reader, but it also means that everything the reader might need is always available, and the syntactical patterns are unique enough that it is "easy" (we can argue this point forever) to skip things you don't care about. When I look at type signatures, sometimes I care about the trait bounds, sometimes I don't. Sometimes I care about the lifetime relationship between different arguments and the output, sometimes I don't. Languages that make these relationships completely implicit make it easy to focus on some aspects of the code's behavior, while obscuring others.


It's hard to optimize for readability, performance, and safety. Rust chose to go with performance and safety. In the future, maybe we can have a language that gives all three but not today.


Shameless relevant plug: that's the exact goal of Vale! [0]

It turns out, when one removes the borrow checker, they get something that's much more readable, because a lot of Rust's complexity was added to help support the borrow checker.

Ironically, we can then add back in a different, easier form of borrow checking to get the speed benefits.

[0] https://vale.dev/


> Rust is also very difficult. It's as complex as C++, and throws it all at the programmer at once.

I thin Rust isnt nearly as complex


It definitely is. You've probably just had more time to discover the complexity of C++ (and read about it, since it's actually specified).

Of course Rust's complexity is much less dangerous because if you forget some obscure rules you get a compile error instead of UB (in safe Rust at least).


Vale sounds very promising (I'm also closely following Koka). But one thing I've found is that Rust's ownership/borrowing model does more than just eliminate GC. It also seems to encourage good program structure that is less prone to logic bugs.

I don't have solid evidence for that - more of a feeling. But I wonder if switching to reference counting would lose that.

Anyway, just an observation.


It's refreshing to see such a simple, good-looking and informative website. Also incredibly fast! Please keep it that way.


Vale looks exciting. Thanks for the link.


Same thing that happened with type declarations will need to happen to semantic intent: inference.


You've repeated this in several comments. I seem to have a completely different experience to you because I find it extremely easy to read and understand rust. For sure, some constructs are tricky, like heavily generic iterators or something, but that's not rust so much as the concept that's tricky.


How would you change the syntax?

I don't think that Rust has much redundant syntax.

I guess you could do things like replace &'a Type with Ref<'a, Type> and *Type with Ptr<Type>, and get rid of some sugar like "if let" and print!, but I'm not sure that would have much of an impact.


Correct, this is more or less like remarking that having to learn Kanji/Hanzi makes learning Japanese/Mandarin very difficult is a superficial complaint.


> but the combinatorial explosion of syntax that supports them is untenable.

I wouldn't go quite that far myself, but it's definitely one of the sharper edges of the language currently--particularly because some of the features don't work together yet. E.g., async and traits.


I use rust weekly and I find it to have the best DX. I have done work with Oracle Java 5-8, IBM XL C99, MSVC++11, CPython 2-3, C# .NET Core 3.1. Stable Rust 2021 is overall the most readable, least surprising, BUT only with the right tool which also makes it the most discoverable, with rust-analyzer. My only gripe is the lack of consensus on strongly typed error handling (anyhow+thiserror being the most sensible combination I found after moving away from bare Results, to failure, to just anyhow).


I find Rust code hard to read...to the point where I don't feel motivated to learn it anymore. Line noise is confusing and a distraction. Random syntactic "innovations" I find are just friction in picking up a language.

For example, in the first versions of Virgil I introduced new keywords for declaring fields: "field", "method" and then "local". There was a different syntax for switch statements, a slightly different syntax for array accesses. Then I looked at the code I was writing and realized that the different keywords didn't add anything, the array subscripting syntax was just a bother; in fact, all my "innovations" just took things away and made it harder to learn.

For better or for worse, the world is starting to converge on something that looks like an amalgam of Java, JavaScript, and Scala. At least IMHO; that's kind of what Virgil has started to look like, heh :)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: