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

Every time I consider learning Rust, I am thrown back by how... "janky" the syntax is. It seems to me that we ought to have a system-level language which builds upon the learnings of the past 20+ years. Can someone help me understand this? Why are we pushing forward with a language that has a Perl-esque unreadability...?

Comparison: I often program in Python (and teach it) - and while it has its own syntax warts & frustrations - overall the language has a "pseudocode which compiles" approach, which I appreciate. Similarly, I appreciate what Kotlin has done with Java. Is there a "Kotlin for Rust"? or another high quality system language we ought to be investing in? I genuinely believe that languages ought to start with "newbie friendliness", and would love to hear challenges to that idea.





You might this blog post interesting, which argues that it's Rust semantics and not syntax that results in the noisiness, i.e.: it's intrinsic complexity:

https://matklad.github.io/2023/01/26/rusts-ugly-syntax.html

I found it reasonably convincing. For what it's worth, I found Rust's syntax quite daunting at first (coming from Python as well), but it only took a few months of continuous use to get used to it. I think "Perl-esque" is an overstatement.

It has some upsides over Python as well, notably that the lack of significant whitespace means inserting a small change and letting the autoformatter deal with syntax changes is quite easy, whereas in Python I occasionally have to faff with indentation before Black/Ruff will let me autoformat.

I appreciate that for teaching, the trade-offs go in the other direction.


I'm not sure which of the dozen Rust-syntax supporters I should reply to, but consider something like these three (probably equivalent) syntaxes:

    let mut a = Vec::<u32>::new();
    let mut b = <Vec::<u32>>::new();
    let mut c = <Vec<u32>>::new();
    let mut d: Vec<u32> = Vec::new();
Which one will your coworker choose? What will your other corworkers choose?

This is day one stuff for declaring a dynamic array. What you really want is something like:

    let mut z = Vec<u32>::new();
However, the grammar is problematic here because of using less-than and greater-than as brackets in a type "context". You can explain that as either not learning from C++'s mistakes or trying to appeal to a C++ audience I guess.

Yes, I know there is a `vec!` macro. Will you require your coworkers to declare a similar macro when they start to implement their own generic types?

There are lots of other examples when you get to what traits are required to satisfy generics ("where clauses" vs "bounds"), or the lifetime signature stuff and so on...

You can argue that strong typing has some intrinsic complexity, but it's tougher to defend the multiple ways to do things, and that WAS one of Perl's mantras.


This is like complaining that in C you can write

    a->b
    (a->b)
    (*a).b
    ((*a).b)
Being able to use disambiguated syntaxes, and being able to add extra brackets, isn't an issue.

PS. The formatting tooling normalizes your second and third example to the same syntax. Personally I think it ought to normalize both of them to the first syntax as well, but it's not particularly surprising that it doesn't because they aren't things anyone ever writes.


> This is like complaining that in C [...]

It's really not. Only one of my examples has the equivalent of superfluous parens, and none are dereferencing anything. And I'm not defending C or C++ anyways.

When I was trying to learn Rust (the second time), I wanted to know how to make my own types. As such, the macro `vec!` mentioned elsewhere isn't really relevant. I was using `Vec` to figure things out so I could make a `FingerTree`:

    let v: Vec<u32> = Vec::new();  // Awfully Java-like in repeating myself

    let v = Vec::new(); // Crap, I want to specify the type of Vec

    let v = Vec<u32>::new();  // Crap, that doesn't compile.
And so on...

> let v = Vec::new(); // Crap, I want to specify the type of Vec

This kinda implies you've gone wrong somewhere. That doesn't mean there aren't cases where you need type annotations (they certainly exist!) but that if `Vec::new()` doesn't compile because the compiler couldn't deduce the type, it implies something is off with your code.

It's impossible to tell you exactly what the problem was, just that `<Vec<T>>::new()` is not code that you would ever see in a Rust codebase.


Nah, there's lots of times you need to specify the types of Vec, either because

1. You don't want the default `i32` integer type and this is just a temporary vector of integers.

2. Rust's type inference is not perfect and sometimes the compiler will object even though there's only one type that could possibly work.

Edit: The <Vec<T>>::new() syntax is definitely never used though.


Or just collect::<Vec<_>>() when you have up doing everything in a lazy pattern and want a concrete type again.

Which I guess i typical stumbling block when the compiler can’t infer what type to collect into.


Most likely

    let e = Vec::new()
or

    let f = vec![]
rustc will figure out the type

exactly. you specify types for function parameters and structs and let the language do it's thing. it's a bit of a niche to specify a type within a function...

There is a reason the multiple methods detailed above exist. Mostly for random iterator syntax. Such as summing an array or calling collect on an iterator. Most Rust devs probably don't use all of these syntax in a single year or maybe even their careers.


Do these print statements print the same thing?

    let i = 1;
    let j = 1;
    print!("i: {:?}\n", !i);
    print!("j: {:?}\n", !j);

    let v = vec![1, 2, 3];
    v[i];
There are definitely times you want to specify a type.

> There are definitely times you want to specify a type.

So I'm coming from basically obly TypeScript type system experience but that seems completely ok to me. There are times I make my TS uglier to make it less ambiguous and times I make it more ambiguous to make it more readable. It's unreasonable imo that such a system could universally land on the most readable format even if we could all agree what's most readable. Instead, some cases are going to be tradeoffs so that the more common cases can flow unimpeded.


The Rust example I showed changes behavior when you declare the types. It's not just a readability or bug catching thing.

I can't believe that a flexible powerful syntax is considered limiting or confusing by some people. There is way more confusing edge-case syntax keywords in C++ that are huge foot-guns.

> Which one will your coworker choose? What will your other corworkers choose?

I don’t think I’ve ever seen the second two syntaxes anywhere.

I really don’t think this is a problem.


I've only ever seen `a` and `d`. Personally I prefer `a`. The only time I've seen `c` is for trait methods like `<Self as Trait<Generic>>::func`. Noisy? I guess. Not sure how else this could really be written.

Fwiw, I didn't go looking for obscure examples to make HN posts. I've had three rounds of sincerely trying to really learn and understand Rust. The first was back when pointer types had sigils, but this exact declaration was my first stumbling block on my second time around.

The first version I got working was `d`, and my first thought was, "you're kidding me - the right hand side is inferring it's type from the left?!?" I didn't learn about "turbo fish" until some time later.


Rust’s inference is generally a strength. If there's a type-shaped hole to fill, and only one way to fill it, Rust will just do it. So for instance `takes_a_vec(some_iter.collect())` works even though `collect` has a generic return type — being passed to `takes_a_vec` implies it must be a Vec, and so that's what Rust infers.

> The first version I got working was `d`, and my first thought was, "you're kidding me - the right hand side is inferring it's type from the left?!?" I didn't learn about "turbo fish" until some time later.

Tbh d strikes me as the most normal - right hand sides inferring the type from the left exists in basically every typed language. Consider for instance the C code

    some_struct a = { .flag = true, .value = 123, .stuff = 0.456 };
Doing this inference at a distance is more of a feature of the sml languages (though I think it now exists even in C with `auto`) - but just going from left to right is... normal.

I see your point, and it's a nice example, but not completely parallel to the Rust/StandardML thing. Here, your RHS is an initializer, not a value.

    // I don't think this flies in C or C++,
    // even with "designated initializers":
    f({ .flag = true, .value = 123, .stuff=0.456});

    // Both of these "probably" do work:
    f((some_struct){ .flag = true, ... });
    f(some_struct{ .flag = true, ... });

    // So this should work too:
    auto a = (some_struct){ .flag = true, ... };
Take all that with a grain of salt. I didn't try to compile any of it for this reply.

Anyways, I only touched SML briefly 30 some years ago, and my reaction to this level of type inference sophistication in Rust went through phases of initial astonishment, quickly embracing it, and eventually being annoyed at it. Just like data flows from expressions calculating values, I like it when the type inference flows in similarly obvious ways.


This will be the case in any language with both generics and type inference. It's nothing to do specifically with Rust.

I mean, the fact that you mention "probably equivalent" is part of the reality here: Nobody writes the majority of these forms in real code. They are equivalent, by the way.

In real code, the only form I've ever seen out of these in the wild is your d form.


Agree. This isn't really a problem unless you also think that extra parentheses is a problem.

In many languages you could write:

> if (a + b) > (c + d)

or

> if a + b > c + d

And they're equivalent. Yet nobody complains that there are too many options.


This is some True Scotsman style counter argument, and it's hard for me to make a polite reply to it.

There are people who program with a "fake it till you make it" approach, cutting and pasting from Stack Overflow, and hoping the compiler errors are enough to fix their mess. Historically, these are the ones your pages/books cater to, and the ones who think the borrow checker is the hard part. It doesn't surprise me that you only see code from that kind of beginner and experts on some rust-dev forum and nothing in between.


The issue though is that this isn't a solvable "problem". This is how programming languages' syntax work. It's like saying that C's if syntax is bad because these are equivalent:

  if (x > y) {

  if ((x > y)) {

  if (((x) > (y))) {
Yes, one of your co-workers may write the third form. But it's just not possible for a programming language to stop this from existing, or at least, maybe you could do it, but it would add a ton of complexity for something that in practice isn't a problem.

Only `b` has the equivalent of "superfluous parens".

It's practically your job to defend Rust, so I don't expect you to budge even one inch. However, I hate the idea of letting you mislead the casual reader that this is somehow equivalent and "just how languages work".

The grammar could've used `Generic[Specific]` with square brackets and avoided the need for the turbo fish.


It hasn't been my job to work on Rust in for years now. And even then, it was not to "defend" Rust, but to write docs. I talk about it on my own time, and I have often advocated for change in Rust based on my conversations with users.

If you're being overly literal, yes, the <>s are needed here for this exact syntax. My point was not about this specific example, it's that these forms are equivalent, but some of them are syntactically simpler than others. The existence of redundant forms does not make the syntax illegitimate, or overly complex.

For this specific issue, if square brackets were used for generics, then something else would have to change for array indexing, and folks would be complaining that Rust doesn't do what every other language does here, which is its own problem.


> For this specific issue, if square brackets were used for generics, then something else would have to change for array indexing

The compiler knows when the `A` in `A[B]` is a type vs a variable.


A compiler could disambiguate, but the goal is to have parsing happen without knowing if A is a type or a variable. That is the inappropriate intertwining of parsing and semantics that languages are interested in getting away from, not continuing with.

Anyway, just to be clear: not liking the turbofish is fine, it's a subjective preference. But it's not an objective win, that's all I'm saying. And it's only one small corner of Rust's syntax, so I don't think that removing it would really alleviate the sorts of broad objections that the original parent was talking about.


> The grammar could've used `Generic[Specific]` with square brackets and avoided the need for the turbo fish.

But then people would grouse about it using left-bracket and right-bracket as brackets in a type "context".


The problem here is that angle brackets are semantics dependent syntax. Whether they are brackets or not depends on semantic context. Conversely square brackets are always brackets.

Square brackets would be semantically dependent if they appeared in the same position of angle brackets. There's nothing magical about [] that makes the problems with <> disappear.

It disappears the problem that angle brackets are sometimes not brackets. I.e. a<b>c is parsed as (a<b)>c or as (a(<b>))c.

It also comes up when you want compile time expressions as parameters to your generics:

    // nice and clean
    let a = Generic[T, A > B]::new(); 

    // gross curlies needed because of poor choices
    let a = Generic::<T, {A > B}>::new();

So that’s the Specificth element of Generic?

It's Brackets(Generic,Specific).

Lol, yes they would. However, I wouldn't. :-)

Well, the solution usually isn't in syntax, but it often is solved by way of code formatters, which can normalize the syntax to a preferred form among several equivalent options.

I certainly would support rustfmt turning those redundant forms into the simpler one.

I suspect rustfmt would consider this out of scope, but there should be a more... "adventurous" code formatter that does more opinionated changes. On the other hand, you could write a clippy lint today and rely on rustfix instead

I think Perl-esque is apt, but that's because I've done quite a bit of Perl and think the syntax concerns are overblown. Once you get past the sigils on the variables Perl's syntax is generally pretty straightforward, albeit with a few warts in places like almost every language. The other area where people complained about Perl's opaqueness was the regular expressions, which most languages picked up anyway because people realized just how useful they are.

That's it exactly.

Once you're writing Rust at full speed, you'll find you won't be putting lifetimes and trait bounds on everything. Some of this becomes implicit, some of it you can just avoid with simpler patterns.

When you write Rust code without lifetimes and trait bounds and nested types, the language looks like Ruby lite.

When you write Rust code with traits or nested types, it looks like Java + Ruby.

When you sprinkle in the lifetimes, it takes on a bit of character of its own.

It honestly isn't hard to read once you use the language a lot. Imagine what Python looks like to a day zero newbie vs. a seasoned python developer.

You can constrain complexity (if you even need it) to certain modules, leaving other code relatively clean. Imagine the Python modules that use all the language features - you've seen them!

One of the best hacks of all: if you're writing HTTP services, you might be able to write nearly 100% of your code without lifetimes at all. Because almost everything happening in request flow is linear and not shared.


>When you write Rust code without lifetimes and trait bounds and nested types, the language looks like Ruby lite.

And once you learn a few idioms this is mostly the default.


This honestly reads like the cliche "you just don't get it yet" dismissals of many rust criticisms.

Not at all!

I'm trying to sell Rust to someone who is worried about it. I'm not trying to sound elitist. I want people to try it and like it. It's a useful tool. I want more people to have it. And that's not scaring people away.

Rust isn't as hard or as bad as you think. It just takes time to let it sink in. It's got a little bit of a learning curve, but that pain goes away pretty quick.

Once you've paid that down, Rust is friendly and easy. My biggest gripe with Rust is compile times with Serde and proc macros.


> Rust isn't as hard or as bad as you think.

I think this depends a LOT on what you're trying to do and what you need to learn to do it. If you can get by with the std/core types and are happy with various third party crates, then you don't really need to learn the language very deeply.

However, if you want to implement new data structures or generic algorithms, it gets very deep very quickly.


Why would you say that? I feel this pushes people away.

"Hey, you might be able to use Rust trivially if you stick to XYZ, but if you dare touch systems programming you're in for some real hurt. Dragons everywhere."

Why say that? It's not even remotely true - it's a gradient of learning. You can use Rust for simple problems as a gateway into systems programming.

Rust is honestly a great alternative to Python or Golang for writing servers. Especially given that you can deploy static binaries or WASM.

We need more people learning the language, not to scare them away.

Rust is getting easier year over year, too! People can choose Rust for their problems today and not struggle.

Give them a cookie and let them see for themselves.


> Why would you say that? I feel this pushes people away.

It's not my obligation to evangelize for your pet language. I've spent enough time and written enough code in Rust to have a defensible viewpoint. This is public forum - I'll share my opinion if I want to.

Writing servers? Sure, go grab the crate that solves your problem and get on with it. Basically what I said above.

If I thought you would bother to do them, I could give you a list of concrete problems which ought to be super easy but are in fact really hard or ugly to do in Rust.

Phrases like "systems programming" have become so diluted that I'm not even sure what you mean. Once upon a time, that was something like writing a device driver. Now people use the phrase for things like parsing a log file or providing a web server.

I wanted to use Rust for numerical methods and data visualization. I didn't like the existing solutions, so I was willing to write my own Rust libraries from scratch. It was pretty painful, and the learning curve was steep.

> Why say that? It's not even remotely true

I didn't write the thing you quoted. Using a straw man argument like this is a lame tactic.


That article is really good, because it highlight that Rust doesn't have to look messy. Part of the problem, I think, is that there's a few to many people who think that messy version is better, because it "uses more of the language" and it makes them look smarter. Or maybe Rust just makes it to hard to see through the semantics and realize that just because feature is there doesn't mean that you need it.

There's also a massive difference between the type of C or Perl someone like me would write, versus someone trying to cope with a more hostile environment or who requires higher levels of performance. My code might be easier to read, but it technically has issue, they are mostly not relevant, while the reverse is true for a more skilled developer, in a different environment. Rust seems to attract really skilled people, who have really defensive code styles or who use more of the provided language features, and that makes to code harder to read, but that would also be the case in e.g. C++.


> I am thrown back by how... "janky" the syntax is.

Well if you come from C++ it's a breath of fresh air! Rust is like a "cleaned-up" C++, that does not carry the historical baggage forced by backwards compatibility. It is well-thought out from the start. The syntax may appear a bit too synthetic; but that's just the first day of use. If you use it for a few days, you'll soon find that it's a great, beautiful language!

The main problem with rust is that the community around it has embraced all the toxic traditions of the js/node ecosystem, and then some. Cargo is a terrifying nightmare. If you could install regular rust dependencies with "apt install" in debian stable, that would be a different story! But no. They want the version churn: continuously adding and removing bugs, like particle/anti-particle pairs at the boundary of a black hole.

Concerning TFA, adding rust to apt might be a step in the right direction. But it should be symmetric: apt depends on rust, that's great! But all the rust that it depends on needs to be installed by apt, and by apt alone!


I am coming from C++ and think Cargo is a blessing.

I like that I can just add a dependency and be done instead of having to deal with dependencies which require downloading stuff from the internet and making them discoverable for the project specific tool chain - which works differently on every operating system.

Same goes for compiling other projects.


While it kinda flies under the radar, most modern C projects do have a kind of package management solution in the form of pkg-config. Instead of the wild west of downloading and installing every dependency and figuring out how to integrate it properly with the OS and your project you can add a bit of syntactic sugar to your Makefile and have that mostly handled for you, save for the part where you will need to use your platform's native package manager to install the dependencies first. On a modern system using a package on a C project just requires a Makefile that looks something like this:

    CC=clang
    MODULES=glib-2.0 atk
    CFLAGS=-g -Wall -pedantic --std=c17 `pkg-config --cflags $(MODULES)`
    LDLIBS=`pkg-config --libs $(MODULES)`

    ALL: myapp

    myapp: myapp.c utils.c io.c

Unless you are using nix or something, pkg-config is comparing apples to oranges.

But that is the kind of convenience and ease of use that brings us another npm malware incident every other month at this point.

This is a real problem but I wouldn't blame the existence of good tooling on it. Sure you don't have this issue with C or C++, but thats because adding even a single dependency to a C or C++ project sucks, the tooling sucks.

I wholly blame developers who are too eager to just pull new dependencies in when they could've just written 7 lines themselves.


I remember hearing a few years ago about how developers considered every line of code the wrote as a failing and talked about how modern development was just gluing otherwise maintained modules together to avoid having to maintain their own project. I thought this sounded insane and I still do.

And in a way I think AI can help here, where instead you get just the snippet vs having to add that dep that then becomes a long-term security liability

On the other hand you don't have developers handrolling their own shitty versions of common things like hashmaps or json-serializers, just because the dependencies are to hard to integrate.

Debian already builds Rust packages from apt, so it will satisfy that critera.

> The main problem with rust is that the community around it has embraced all the toxic traditions of the js/node ecosystem, and then some. Cargo is a terrifying nightmare. If you could install regular rust dependencies with "apt install" in debian stable, that would be a different story! But no. They want the version churn: continuously adding and removing bugs, like particle/anti-particle pairs at the boundary of a black hole.

Something I didn't appreciate for a long time is that, the C/C++ ecosystem does have an npm-like package management ecosystem - it is just implemented at the level of Linux distro maintainers DDD deciding what to package and how. Which worked ok because C was the lingua franca of Unix systems.

But actually it's valuable for programmers to be able to specify their dependencies for their own projects and update them on a schedule unconnected and uncoordinated with the OS's releases. The cargo/npm model is closer to ideal.

Of course what is even better is NixOS-like declarative specification and hashing of all dependencies


As a c/c++ cmake user, cargo sounds like a utopia in comparison. It still amazes me that c/c++ package management is still spread between about 5 different solutions.

IMO, the biggest improvement to C/C++ would be ISO defining a package manager a.la pip or uv or cargo. I'm so tired of writing cmake. just... tired.


People that don't understand make are destined to recreate it poorly, and there's no better example than cmake, imho.

Here's my arc through C/C++ build systems:

- make (copy pasted examples)

- RTFM [1]

- recursive make for all sorts of non-build purposes - this is as good as hadoop up to about 16 machines

- autotools

- cmake

- read "recursive make considered harmful" [2]

- make + templates

Anyway, once you've understood [1] and [2], it's pretty hard to justify cmake over make + manual vendoring. If you need windows + linux builds (cmake's most-advertised feature), you'll pretty quickly realize the VS projects it produces are a hot mess, and wonder why you don't just maintain a separate build config for windows.

[1] https://www.gnu.org/software/make/manual/

[2] https://news.ycombinator.com/item?id=20014348

If I was going to try to improve on the state of the art, I'd clean up a few corner cases in make semantics where it misses productions in complicated corner cases (the problems are analogous to prolog vs datalog), and then fix the macro syntax.

If you want a good package manager for C/C++, check out Debian or its derivatives. (I'm serious -- if you're upset about the lack of packages, there's a pretty obvious solution. Now that docker exists, the packages run most places. Support for some sort of AppImage style installer would be nice for use with lesser distros.)


cmake exists not because people didn't understand make, but because there was no one make to understand. The "c" is for "cross platform." It's a replacement for autoconf/automake, not a replacement for make.

> If I was going to try to improve on the state of the art

The state of the art is buck/bazel/nix/build2.


Do note that a (I think standardized) common package specification is being worked on called CPS (Common Package Specification). It doesn't specify how you get your dependencies, but it does specify how they should look like, so that your actual package manager does not need to care about the build system specific formats as it currently does.

Worth reading, from a year ago: https://www.reddit.com/r/cpp/comments/1hgpz0y/wg21_aka_c_sta...

It seems CPS is still being worked on, but not under the standardization of the committee, due to the above.


cmake is a self-inflicted problem of some C++ users, and an independent issue of the language itself (just like cargo for rust). If you want, you can use a makefile and distribution-provided dependencies, or vendored dependencies, and you don't need cmake.

imo the biggest single problem with C++ that the simple act of building it is not (and it seems, cannot) be standardized.

This creates kind of geographic barriers that segregate populations of C++ users, and just like any language, that isolation begets dialects and idioms that are foreign to anyone from a different group.

But the stewards of the language seem to pretend these barriers don't exist, or at least don't understand them, and go on to make the mountain ranges separating our valleys even steeper.

So it's not that CMake is a self-inflicted wound. It's the natural evolution of a tool to fill in the gaps left under specified by the language developers.


> Cargo is a terrifying nightmare

Really? Why? I'm not a Rust guru, but Cargo is the only part of Rust that gave me a great first impression.


GP mostly answered that in the comment already:

> If you could install regular rust dependencies with "apt install" in debian stable, that would be a different story! But no. They want the version churn: continuously adding and removing bugs, like particle/anti-particle pairs at the boundary of a black hole.


I don't know, it doesn't explain how and why Cargo causes "continuously adding and removing bugs, like particle/anti-particle pairs at the boundary of a black hole."

They are conflating unrelated things. Cargo is a downstream result of the thing that annoys them, not the cause. What they don’t like is that rust is statically linked with strong versioned dependencies. There are pros and cons to that, but one outcome (which some list as pro and some list as con) is that you need to recompile world for every project. Hence, cargo.

The problem, of course, is that "apt install" only works on platforms that use apt to manage their packages.

Depending on the particular apt repo to be ready and have what you need would be so much more of a nightmare!

> Rust is like a "cleaned-up" C++

Except they got the order of type and variable wrong. That alone is enough reason to never use Rust, Go, TypeScript or any other language that botches such a critical cornerstone of language syntax.


[flagged]


That was needlessly rude.

> Comparison: I often program in Python (and teach it) - and while it has its own syntax warts & frustrations - overall the language has a "pseudocode which compiles" approach, which I appreciate.

I think this is why you don’t like Rust: In Rust you have to be explicit by design. Being explicit adds syntax.

If you appreciate languages where you can write pseudocode and have the details handled automatically for you, then you’re probably not going to enjoy any language that expects you to be explicit about details.

As far as “janky syntax”, that’s a matter of perspective. Every time I deal with Python and do things like “__slots__” it feels like janky layer upon layer of ideas added on top of a language that has evolved to support things it wasn’t originally planned to do, which feels janky to me. All of the things I have to do in order to get a performant Python program feel incredibly janky relative to using a language with first class support for the things I need to do.


> Being explicit adds syntax.

Not what they are talking about. Rather better to use words instead of symbols, like python over perl.

Instead of “turbofish” and <‘a>, there could be more key words like mut or dyn. Semicolons and ‘c’har are straight out of the seventies as well. :: not useful and ugly, etc.

Dunders avoid namespace collisions and are not a big problem in practice, all one char, and easy to read. I might remove the trailing part if I had the power.


This is just personal preferences and familiarity

Python using indenting to convey specific programming meaning feels janky and outdated to people not familiar with Python, but Python familiar programmers don't think twice about it.


I've been relying on Python for a decade+ and still think twice about the indentation. Straight up bad design, and you can't even attribute it to the original use case of dirty scripting cause it's particularly bad in a REPL.

No, it’s not only familiarity. I learned C/C++ in the early 90s, before Python.

It’s well studied that words are easier to read than nested symbols.


Maybe it's my math background but I honestly prefer symbols to keywords. It's more up front cost in learning, but it's much more efficient in the long run.

When you are doing multiple operations to multiple variables, and need to see it all at once, math-like syntax still has benefits.

But this is not the common case for most programming, which is about detailing business rules. Explicit and verbose (though not excessively) has been shown to be the most readable/maintainable. For example, one character variable names, common in math, are heavily discouraged in professional development.

There’s another level to this as well. To me, calculus notation looks quite elegant, while Perl and (parts of) Rust look like trash. Since they are somewhat similar, the remaining angle is good taste.


Both Python and JS evolved by building on top of older versions, but somehow JS did a way better job than Python, even though Py forced a major breaking change.

Agree about Rust, all the syntax is necessary for what it's trying to do.


JS is not something I would hold up in high regard.

You mean typescript?

Before that. The classes and stuff added in ES6 and earlier

Syntax tends to be deeply personal. I would say the most straightforward answer to your question is "many people disagree that it is unreadable."

Rust did build on the learnings of the past 20 years. Essentially all of its syntax was taken from other languages, even lifetimes.


Are the many who disagree that it is unreadable more than the people who agree? I have been involved with the language for a while now, and while I appreciate what you and many others have done for it, the sense that the group is immune to feedback just becomes too palpable too often. That, and the really aggressive PR.

Rust is trying to solve a really important problem, and so far it might well be one of the best solutions we have for it in a general sense. I 100% support its use in as many places as possible, so that it can evolve. However, its evolution seems to be thwarted by a very vocal subset of its leadership and community who have made it a part of their identity and whatever socio-political leverage toolset they use.


I've found the rust core team to be very open to feedback. And maybe I've just been using Rust for too long, but the syntax feels quite reasonable to me.

Just for my own curiosity, do you have an examples of suggestions for how to improve the syntax that have been brought up and dismissed by the language maintainers?


> Are the many who disagree that it is unreadable more than the people who agree?

I have no way to properly evaluate that statement. My gut says no, because I see people complain about other things far more often, but I do think it's unknowable.

I'm not involved with Rust any more, and I also agree with you that sometimes Rust leadership can be insular and opaque. But the parent isn't really feedback. It's just a complaint. There's nothing actionable to do here. In fact, when I read the parent's post, I said "hm, I'm not that familiar with Kotlin actually, maybe I'll go check it out," loaded up https://kotlinlang.org/docs/basic-syntax.html, and frankly, it looks a lot like Rust.

But even beyond that: it's not reasonably possible to change a language's entire syntax ten years post 1.0. Sure, you can make tweaks, but turning Rust into Python simply is not going to happen. It would be irresponsible.


> the sense that the group is immune to feedback

Is complaining about syntax really productive though? What is really going to be done about it?


This is such a weird take. What do you suggest? Should Rust’s syntax have been democratically decided?

Rust is almost git hyoe 2.0. That hyoe set the world up with (a) a dominant VCS that is spectacularly bad at almost everything it does compared to its competitors and (b) the dominant Github social network owned by MS that got ripped to train Copilot.

Developers have a way of running with a hyoe that can be quite disturbing and detrimental in the long run. The one difference here is that rust has some solid ideas implemented underneath. But the community proselytizing and throwing non-believers under the bus is quite real.


The lifetime syntax was taken from OCaml but it has somewhat different semarics than OCaml. I honestly get a bit tripped up when I look at OCaml code (a language I'm a beginner at), and see ordinary parameterized types using syntax that suggests to me, from a Rust background, "woah, complex lifetime situation ahead!"

I know that Graydon Hoare is a fan of OCaml and that it was a core inspiration for Rust, and I sometimes wonder if he gets tripped up too by having to switch between Rust-inspired and OCaml-inspired interpretations of the same characters.


It's similar but different: both are type variables, but it's true that it's used for the "other" type variables in Rust.

For what it's worth, I am not even sure that Graydon was the one who introduced lifetime syntax. He was a fan of terseness, though: Rust's keywords used to be all five characters or shorter.

Niko and pcwalton were the ones working on regions, Niko talks a little bit about the motivation for syntax here: https://smallcultfollowing.com/babysteps/blog/2012/03/28/avo...

Later posts include /& as syntax: https://smallcultfollowing.com/babysteps/blog/2012/04/25/ref...

Eventually, another syntax: https://smallcultfollowing.com/babysteps/blog/2012/07/10/bor... which turns into a &x/ syntax: https://smallcultfollowing.com/babysteps/blog/2012/07/17/bor...

Which turns into this one, talking about variants of possible syntax: https://smallcultfollowing.com/babysteps/blog/2012/12/30/lif...

At some point, we get the current syntax: https://smallcultfollowing.com/babysteps/blog/2013/04/04/nes...

So, it happened somewhere in here...


There’s syntax that is objectively easier to both read and write, and there’s syntax that is both harder to read and write. For a majority.

In general, using english words consisting of a-z is easier to read. Using regex-like mojibake is harder.

For an concrete example in rust, using pipes in lambdas, instead of an arrow, is aweful.


Rust's pipes in lambdas come from Ruby, a language that's often regarded as having beautiful syntax.

Rust is objectively not mojibake. The equivalent here would be like using a-z, as Rust's syntax is borrowed from other languages in wide use, not anything particularly esoteric. (Unless you could OCaml as esoteric, which I do believe is somewhat arguable but that's only one thing, the argument still holds for the vast majority of the language.)


> In general, using english words consisting of a-z is easier to read.

I’ve seen COBOL in the wild. No thanks.

But also, imagine reading a math proof written in English words. That just doesn’t work well.


I don't think it's an awful choice, but I'll admit that pipes in lambdas are not my favorite bit of syntax. I'm not a fan of them in Ruby either. I personally prefer JavaScript-ish => for lambdas. But I'm not gonna try to bikeshed one syntax decision made over a decade ago that has relatively minor consequences for other parts of the language. The early Rust core team had different taste than I do essentially, and that's fine.

uuuh I like the pipes even though its my first language with them?

Concise and much clearer to read vs parentheses where you gotta wonder if the params are just arguments, or a tuple, etc. What are you talking about.


I’ve been writing python professionally for over 10 years. In the last year I’ve been writing more and most Rust. At first I thought the same as you. It’s a fugly language, there’s no denying it. But once I started to learn what all the weird syntax was for, it began to ruin Python for me.

Now I begrudge any time I have to go back to python. It feels like its beauty is only skin deep, but the ugly details are right there beneath the surface: prolific duck typing, exceptions as control flow, dynamic attributes. All these now make me uneasy, like I can’t be sure what my code will really do at runtime.

Rust is ugly but it’s telling you exactly what it will do.


>Now I begrudge any time I have to go back to python. It feels like its beauty is only skin deep, but the ugly details are right there beneath the surface: prolific duck typing, exceptions as control flow, dynamic attributes. All these now make me uneasy, like I can’t be sure what my code will really do at runtime.

I feel like this sentiment is from people who haven't really took the time to fully see what the Python ecosystem is.

Any language can have shittly written code. However languages that by default disallow it means that you have to spend extra time prototyping things, whereas in Python, you can often make things work without much issue. Dynamic typing and attributes make the language very flexible and easily adaptable.


Oh I’m familiar with the ecosystem. Yes the dynamic nature does make it easy to prototype things flexibly. The problem is when your coworker, or you, decide to flexibly and dynamically get the job on a Friday before a long weekend and then 3 months later you need to figure out how a variable is being set, or where a method is being called.

And thats no different than writing Rust with a bunch of unsafes, and a bunch of indirection as far as processing flow goes.

The nice thing about Python is that it allows you to do either. And naturally, Python has gotten much faster, to the point where its as fast as Java for some things, because when you don't use dynamic typing, it actually recognizes this and optimizes compiled code without having to carry that type information around.


It’s not the same at all. In Rust you cannot just throw an attribute on to a struct in the middle of a function because it makes some call further down the chain easier, no matter how much unsafe you use.

I’m not a python hater, you can’t get some great stuff done with it quickly. But my confidence in writing large complex systems in it is waning.


I can make an argument that you can never have a memory leak in Python, while you can in Rust if you use unsafe.

In the end, both languages allow you to write bad code. But having something that is less strict if you wanna be makes it more flexible.


it's faster to prototype in rust. this may seem counterintuitive, but when a rust program compiles, it generally works. not saying 100% and free from logic bugs, but a lot of the error handling, locks required, and checks for nulls have already been worked out by default.

JS ruined Python for me, cause it serves similar purposes but handles them better. Rust is a different thing, it ruined C and C++ for me.

Those complaints have very little to do with the syntax.

> upon the learnings of the past 20+ years.

That's the thing though... Rust does build on many of those learnings. For starters, managing a big type system is better when some types are implicit, so Rust features type inference to ease the burden in that area. They've also learned from C++'s mistake of having a context sensitive grammar. They learned from C++'s template nightmare error messages so generics are easier to work with. They also applied learnings about immutability being a better default that mutability. The reason Rust is statically linked and packages are managed by a central repository is based on decades of seeing how difficult it is to build and deploy projects in C++, and how easy it is to build and deploy projects in the Node / NPM ecosystem. Pattern matching and tagged unions were added because of how well they worked in functional languages.

As for "Perl-esque unreadability" I submit that it's not unreadable, you are just unfamiliar. I myself find Chinese unreadable, but that doesn't mean Chinese is unreadable.

> Is there a "Kotlin for Rust"?

Kotlin came out 16 years after Java. Rust is relatively new, and it has built on other languages, but it's not the end point. Languages will be written that build on Rust, but that will take some time. Already many nascent projects are out there, but it is yet to be seen which will rise to the top.


I would encourage you to give it a try anyways. Unfamiliar syntax is off-putting for sure, but you can get comfortable with any syntax.

Coming from Python, I needed to work on some legacy Perl code. Perl code looks quite rough to a new user. After time, I got used to it. The syntax becomes a lot less relevant as you spend more time with the language.


Sure... but you don't want to spend time if it's such a mess to read it.

Once one does spend some time to become comfortable with the language, that feeling of messiness with unfamiliar syntax fades away. That's the case with any unfamiliar language, not just Rust.

I used Rust for a year and still wasn't used to the syntax, though this was v1.0 so idk what changed. I see why it's so complicated and would definitely prefer it over C or Cpp, but wouldn't do higher-level code in it.

I'm writing this as a heavy python user in my day job. Python is terrible for writing complex systems in. Both the language and the libraries are full of footguns for the novice and expert alike. It has 20 years of baggage, the packaging and environment handling is nothing short of an unmitigated disaster, although uv seems to be a minor light at the end of the tunnel. It is not a simple language at this point. It has had so many features tacked on, that it needs years of use to have a solid understanding of all the interactions.

Python is a language that became successful not because it was the best in it's class, but because it was the least bad. It became the lingua franca of quantitative analysis, because R was even worse and matlab was a closed ecosystem with strong whiffs of the 80s. It became successful because it was the least bad glue language for getting up and running with ML and later on LLMs.

In comparison, Rust is a very predictable and robust language. The tradeoff it makes is that it buys safety for the price of higher upfront complexity. I'd never use Rust to do research in. It'd be an exercise in frustration. However, for writing reliable and robust systems, it's the least bad currently.


What's wrong with R? I used it and liked it in undergrad. I certainly didn't use it as seriously as the users who made Python popular, but to this day I remember R fondly and would never choose Python for a personal project.

My R use was self-taught, as well. I refused to use proprietary software for school all through high school and university, so I used R where we were expected to use Excel or MatLab (though I usually used GNU Octave for the latter), including for at least one or two math classes. I don't remember anything being tricky or difficult to work with.


R is the most haphazard programming environment I've ever used. It feels like an agglomeration of hundreds of different people's shell aliases and scripting one-liners.

I'll grant my only exposure has been a two- or three-day "Intro to R" class but I ran screaming from that experience and have never touched it again.

It maybe worked against me that I am a programmer, not a statistician or researcher.


When I used it I was a computer science student. But I wasn't reading anyone else's code or trying to maintain anything complex, which is why I asked what I did. I'm sure there are quirks I never had to deal with.

So is it just that the stdlib is really big and messy?


Python had already become vastly popular before ML/AI. Scripting/tools/apps/web/... Only space that hasn't entered is mobile.

Seems like a fairly decent syntax. It’s less simple than many systems languages because it has a very strong type system. That’s a choice of preference in how you want to solve a problem.

I don’t think the memory safety guarantees of Rust could be expressed in the syntax of a language like C or Go.


I code mostly in Go and the typing sloppiness is a major pain point.

Example: You read the expression "x.f", say, in the output of git-diff. Is x a struct object, or a pointer to a struct? Only by referring to enclosing context can you know for sure.


> It’s less simple than many systems languages because it has a very strong type system.

I don’t think that’s the case, somehow most ML derived languages ended up with stronger type system and cleaner syntax.


Is ML a systems language? Sorry, maybe my definition is wrong, but I consider a systems language something that’s used by a decent amount of OS’es, programming languages and OS utilities.

I assume you’re talking about OCaml et al? I’m intruiged by it, but I’m coming from a Haskell/C++ background.

Rust is somewhat unique in terms of system language this because it’s the first one that’s not “simple” like C but still used for systems tools, more than Go is as far as I’m aware.

Which probably has to do with its performance characteristics being close to the machine, which Go cannot do (ie based on LLVM, no GC, etc)


Rust's most complained about syntax, the lifetime syntax, was borrowed from an ML: OCaml.

One of the design goals of rust is explicitness. I think if Rust had type elision, like many other functional languages, it would go a long way to cleaning up the syntax.

There is no other ML-like that is as low level. Except perhaps ATS, which has terrible syntax.

I think the problem for people is the traditional problem that a lot of people have had with a lot of languages since C took off: it doesn't look like ALGOL, and doesn't have the semantics of ALGOL.

The reason python looks like procedural pseudocode is because it was designed to look like procedural pseudocode. Rust is not just a new skin over ALGOL with a few opinionated idioms, it's actually different - mostly to give hints of intention to the compiler, but I don't even think it started from the same place. It's more functional than anything imo, but without caring about purity or appearance, and that resulted in something that superficially looks sufficiently ALGOL-like to confuse people who are used to python or Kotlin.

> I genuinely believe that languages ought to start with "newbie friendliness", and would love to hear challenges to that idea.

In conclusion, I think this is a red herring. Computer languages are hard. What you're actually looking for is something that is ALGOL-like for people who have already done the hard work of learning an ALGOL-like. That's not a newbie, though. Somebody who learned Rust first would make the same complaint about python.


Maybe I've Stockholm'd myself, but I think Rust's syntax is very pleasant. I also think a lot of C code looks very good (although there is some _ugly_ C code out there).

Sometimes the different sets of angle and curly brackets adding up can look ugly at first, and maybe the anonymous function syntax of || {}, but it grows on you if you spend some time with the language (as do all syntaxes, in my experience).


The family of languages that started with ML[0] mostly look like this. Studying that language family will probably help you feel much more at home in Rust.

Many features and stylistic choices from ML derivatives have made their way into Swift, Typescript, and other non-ML languages.

I often say that if you want to be a career programmer, it is a good idea to deeply learn one Lisp-type language (which will help with stuff like Python), one ML-type language (which will help with stuff like Rust) and one C-type language (for obvious reasons.)

[0] https://en.wikipedia.org/wiki/ML_(programming_language)


F# looks nothing like Rust. Is much more readable for me.

F#'s semantics don't describe memory management and lifetimes to the degree that Rust does.

You might enjoy https://nim-lang.org/ which has a Python-like syntax with even more flexibility really (UFCS, command-like calls, `fooTemplate: stuff` like user-defined "statements", user-defined operators, term-rewriting macros and more. With ARC it's really just about as safe as Rust and most of the stdlib is fast by default. "High quality" is kind of subjective, but they are often very welcoming of PRs.

Anyway, to your point, I think a newbie could pick up the basics quickly and later learn more advanced things. In terms of speed, like 3 different times I've compared some Nim impl to a Rust impl and the Nim was faster (though "at the extreme" speed is always more a measure of how much optimization effort has been applied, esp. if the language supports inline assembly).

https://cython.org/ , which is a gradually typed variant of Python that compiles to C, is another decent possibility.


Kotlin programmer here who is picking up Rust recently. you're right, it's no Kotlin when it comes to the elegance of APIs but it's also not too bad at all.

In fact there are some things about the syntax that are actually nice like range syntax, Unit type being (), match expressions, super explicit types, how mutability is represented etc.

I'd argue it's the most similar system level language to Kotlin I've encountered. I encourage you to power through that initial discomfort because in the process it does unlock a level of performance other languages dream of.


What do people actually mean when they say "the syntax is janky"?

I often see comparisons to languages like Python and Kotlin, but both encode far less information on their syntax because they don't have the same features as Rust, so there's no way for them to express the same semantics as rust.

Sure, you can make Rust look simpler by removing information, but at that point you're not just changing syntax, you're changing the language's semantics.

Is there any language that preserves the same level of type information while using a less "janky" syntax?


I don't know if there is, but there certainly needs to be. All of Rust's syntax seems to be needed to support its semantics, but it also undeniably crosses a threshold of "too much syntax". Though I do agree with the person above who said that every language that's not Lisp has too much syntax.

I think this is subjective, because I think Rust's syntax is (mostly) beautiful.

Given the constraint that they had to keep it familiar to C++ people, I'd say they did a wonderful job. It's like C++ meets OCaml.

Do you have any particular complaints about the syntax?


I don’t program much in Rust, but I find it a beautiful syntax… they took C++ and made it pretty much strictly better along with taking some inspiration from ML (which is beautiful imo)

As an official greybeard who has written much in C, C++, Perl, Python, and now Rust, I can say Rust is a wonderful systems programming language. Nothing at all like Perl, and as others have mentioned, a great relief from C++ while providing all the power and low-level bits and bobs important for systems programming.

I prefer Rust syntax to Python's purely on the grounds that Rust is a curly-brace language and Python is an indentation-sensitive language. I like it when the start and end of scopes in code are overtly marked with a non-whitespace character, it reduces the chances of bugs caused by getting confused about what lines of code are in what scope and makes it easier to use text editor tools to move around between scopes.

Beyond that issue, yeah most of Rust's syntactic noise comes from the fact that it is trying to represent genuinely complicated abstractions to support statically-checked memory safety. Any language with a garbage collector doesn't need a big chunk of Rust's syntax.


The rust syntax problem is not the braces but about all the #[()] || ::<<>> ‘stuff.

Attribute, lambda and turbofish?

And 'lifetime. ;-)

> It seems to me that we ought to have a system-level language which builds upon the learnings of the past 20+ years.

Maybe Ada, D or Nim might qualify?


> Why are we pushing forward with a language that has a Perl-esque unreadability...?

The reason is the same for any (including Perl, except those meme languages where obfuscation is a feature) language: the early adopters don't think it's unreadable.


I would argue that anything that is not Lisp has a complicated syntax.

The question is: is it worth it?

With Rust for the answer is yes. The reliability, speed, data-race free nature of the code I get from Rust absolutely justifies the syntax quirks (for me!).


Aside from async/await which I agree is somewhat janky syntaxtically, I'm curious what you consider to be janky. I think Rust is overall pretty nice to read and write. Patterns show up where you want them, type inference is somewhat limited but still useful. Literals are readily available. UFCS is really elegant. I could go on.

Ironically, I find Python syntax frustrating. Imports and list comprehensions read half backwards, variable bindings escape scope, dunder functions, doc comments inside the function, etc.


> Is there a "Kotlin for Rust"?

While it's not a systems language, have you tried Swift?


Swift is as relevant to this discussion as Common Lisp.

On the contrary, Swift is very relevant on this subject. It has high feature parity with rust, with a much readable syntax.

It doesn't have the single feature that anyone cares about in Rust - compiler-enforced ownership semantics. And it's not in any way a system-level language (you couldn't use it without its stdlib for example, like in the Linux kernel).

The other features it shares with Rust are also shared by many other languages.


Compiler-enforced ownership semantics is now a part of Swift with non-copyable types. In all honesty I do not know enough of rust to know how on-par the features are, but there is something.

Not sure about using Swift in a kernel as I’m not low-level enough to know that either, but you can indeed use Swift on embedded systems[1].

[1] https://www.swift.org/get-started/embedded/


But Swift is not "Kotlin for Rust" though, I can't see the connection at all. "Kotlin for Rust" would be a language that keeps you in the Rust ecosystem.

The commenter I replied to seems to like Kotlin. Swift is extremely close to Kotlin in syntax and features, but is not for the JVM. Swift also has a lot of similarities with Rust, if you ignore the fact that it has a garbage collector.

A Kotlin for Rust would be a drop-in replacement where you could have a crate or even just a module written in this hypothetical language and it just works. No bridging or FFI. That’s not Swift.

You all need to stop with the hair-splitting – it's tiresome.

My intention was to offer something that might be of interest to the person I replied to – not to write the official definition of "Kotlin for Rust" which everybody has to agree to. If you think my answer is nonsense, just skip it and read the next one. No need to reply. Nobody profits from this discourse.


Ah yeah ok, makes sense in that way

Have you considered that part of it is not the language but the users?

I'm learning rust and the sample code I frequently find is... cryptically terse. But the (unidiomatic, amateurish) code I write ironically reads a lot better.

I think rust attracts a former c/c++ audience, which then bring the customs of that language here. Something as simple as your variable naming (character vs c, index vs i) can reduce issues already.


Perl’s most notable syntax feature is sigils on all variables.

So it’s strange to hear a comparison. Maybe there’s something I’m missing.

It seems closer to C++ syntax than Perl.


The sigils in Rust (and perl) are there to aid readability. After you use it a bit, you get used to ignoring them unless they look weird.

All the python programs I've had to maintain (I never choose python) have had major maintainability problems due to python's clean looking syntax. I can still look at crazy object oriented perl meta-programming stuff I wrote 20 years ago, and figure out what it's doing.

Golang takes another approach: They impoverished the language until it didn't need fancy syntax to be unambiguously readable. As a workaround, they heavily rely on codegen, so (for instance) Kubernetes is around 2 million lines of code. The lines are mostly readable (even the machine generated ones), but no human is going to be able to read them at the rate they churn.

Anyway, pick your poison, I guess, but there's a reason Rust attracts experienced systems programmers.


> Every time I consider learning Rust, I am thrown back by how... "janky" the syntax is. It seems to me that we ought to have a system-level language which builds upon the learnings of the past 20+ years.

I said this years ago and I was basically told "skill issue". It's unreadable. I shudder to think what it's like to maintain a Rust system at scale.


The syntax has relatively little to do with how easy or hard it is to maintain a rust system at scale. If you get something wrong the compiler will alert you, and most of the syntax is there for good reasons that anyone maintaining any kind of software system at scale needs to understand (and indeed the syntax helps you be clear about what you mean to the compiler, which facilitates helpful compiler error messages if you screw something up when modifying code).

You get used to it. Like any language.

Does it really add any value to the conversation?

what makes it unreadable for you?

Legit question really. A comparative study on language readability using codes doing the same thing written idiomatically in different languages will be interesting. Beyond syntax, idioms/paradigm/familiarity should also play role.

nta you're replying to, but as someone who doesn't know rust, on first glance it seems like it's littered with too many special symbols and very verbose. as i understand it this is required because of the very granular low level control rust offers

maybe unreadable is too strong of a word, but there is a valid point of it looking unapproachable to someone new


I think the main issue people who don't like the syntax have with it is that it's dense. We can imagine a much less dense syntax that preserves the same semantics, but IMO it'd be far worse.

Using matklad's first example from his article on how the issue is more the semantics[1]

    pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
      fn inner(path: &Path) -> io::Result<Vec<u8>> {
        let mut file = File::open(path)?;
        let mut bytes = Vec::new();
        file.read_to_end(&mut bytes)?;
        Ok(bytes)
      }
      inner(path.as_ref())
    }
we can imagine a much less symbol-heavy syntax inspired by POSIX shell, FORTH, & ADA:

    generic
        type P is Path containedBy AsRef
    public function read takes type Path named path returns u8 containedBy Vector containedBy Result fromModule io
      function inner takes type reference to Path named path returns u8 containedBy Vector containedBy Result fromModule io
        try
            let mutable file = path open fromModule File 
        let mutable bytes = new fromModule Vector
        try
            mutable reference to bytes file.read_to_end
        bytes Ok return
      noitcnuf
      path as_ref inner return
    noitcnuf
and I think we'll all agree that's much less readable even though the only punctuation is `=` and `.`. So "symbol heavy" isn't a root cause of the confusion, it's trivial to make worse syntax with fewer symbols. And I like RPN syntax & FORTH.

[1] https://matklad.github.io/2023/01/26/rusts-ugly-syntax.html


That might be an interesting extension to a dev environment or git - convert terse rust into semi-verbose explanation.

Sort of like training wheels, eventually you stop using it.


People often misuse unreadable when they mean unfamiliar. Rust really isn't that difficult to read when you get used to it.

Chinese isn't that difficult to read when you get used to it, too.

> littered with too many special symbols and very verbose

This seems kinda self-contracticting. Special symbols are there to make the syntax terse, not verbose. Perhaps your issue is not with how things are written, but that there's a lot of information for something that seems simpler. In other words a lot of semantic complexity, rather than an issue with syntax.


I think it's also that Rust needs you to be very explicit about things that are very incidental to the intent of your code. In a sense that's true of C, but in C worrying about those things isn't embedded in the syntax, it's in lines of code that are readable (but can also go unwritten or be written wrong). In the GCed languages Rust actually competes with (outside the kernel) — think more like C# or Kotlin, less like Python — you do not have to manage that incidental complexity, which makes Rust look 'janky'.

Python users don’t even believe in enabling cursory type checking, their language design is surpassed even by JavaScript, should it really even be mentioned in a language comparison? It is a tool for ML, nothing else in that language is good or worthwhile

In your opinion how does Rust compare to C++ for readability?

C++ is vastly more readable. I will never go back to writing or maintaining C++ projects, but drop me into a C++ file to review something and it is usually very easy to grok.

Part of this is style and conventions though. I have implemented an STL container before, and that templating hell is far worse than anything I’ve ever seen in the Rust ecosystem. But someone following modern C++ conventions (e.g. a Google library) produces very clean and readable code.


How do you handle understanding the semantics in the presence of custom overloaded operators?

That's not what "readability" generally means. Yes, Rust's semantics are more tightly locked down, and that's a big part of why I use it. But given two well-written source files, one in modern C++ and one in contemporary Rust, can you quickly skim and understand what each one is supposed to be doing, irregardless of bugs that might be lurking? If you made me guess right now which one I'd have an easier time understanding, I'd guess the C++ file.

Honestly, rust is slightly more readable than obfuscated perl. I think I prefer K&R C, and I don't like K&R C. In terms of readability, maybe equivalent to early Win32 API? [3000 lines to set up API, then call to activate].

With C++ you have a range of readable from - easy and very approachable - to 2000s era Microsoft STL. (where not only is it close to unreadable, many many hidden bugs are ... somewhere. And behaviour is not consistent).

I will admit I don't find Rust quite as unreadable as the 2000s era Microsoft STL, but the latter's one of the things that pushed me far more into Linux dev.

Rust is the kind of language that would push me to write a new language that isn't rust. And maybe work on supporting all the zillion platforms rust doesn't support. Or maybe just stick to C. I'm not a fan, but there are worse, and yeah - there are some C++ libraries out there that are worse. Lots that are better, too, eg llvm source.


> Honestly, rust is slightly more readable than obfuscated perl.

> Rust is the kind of language that would push me to write a new language that isn't rust.

It's wild how draconian and forced the use of Rust has become. It's one thing to be caught up in corporate induced hype, yet another level to force one's preferences or ideology on everybody else.

Many people don't like Rust, for various acceptable reasons, and their right to choose should be respected. Sticking with C, is an equally acceptable option. In addition to there being various other newer C alternatives languages; with greater readability, memory-safety, and ease of use. There is also Fil-C[1][2], that can provide memory-safety, with no to minimum changes to the code.

[1]: fil-c.org/

[2]: github.com/pizlonator/fil-c


How much Rust have you written?

What are you talking about? Rust’s function signature and type declaration syntaxes are extremely vanilla, unless you venture into some really extreme use cases with lots of lifetime annotations and generic bounds.

I seriously don’t get it.

    fn add(a: i32, b: i32) -> i32 { … }
Where’s the “Perl-esqueness”?

  trait Handler {
    fn handle<'a>(&self, input: &'a str) -> Result<&'a str, HandlerError>;
  }

  fn process_handler<'a>(
    handler: Box<dyn Handler + 'a>,
    input: &'a str,
  ) -> Result<&'a str, HandlerError> {
    handler.handle(input)
  }

That's just a weird and unrealistic example, though. Like, why is process_handler taking an owned, boxed reference to something it only needs shared access to? Why is there an unnecessary 'a bound on handler?

In the places where you need to add lifetime annotations, it's certainly useful to be able to see them in the types, rather than relegate them to the documentation like in C++; cf. all the places where C++'s STL has to mention iterator and reference invalidation.


LLMs LOVE to write Rust like this. They add smart pointers, options and lifetimes everywhere when none of those things are necessary. I don’t know what it is, but they love over-engineering it.

As a first guess, they're trained on lots of social media and Q&A content. The former has lots of complaints about "look how complex rust is!" while the latter has lots of "help I've written very complex rust".

I agree that the signature for process_handler is weird, but you could steelman it to take a borrowed trait object instead, which would have an extra sigil.

The handler function isn't actually unnecessary, or at least, it isn't superfluous: by default, the signature would include 'a on self as well, and that's probably not what you actually want.

I do think that the example basically boils down to the lifetime syntax though, and yes, while it's a bit odd at first, every other thing that was tried was worse.


> The handler function isn't actually unnecessary, or at least, it isn't superfluous: by default, the signature would include 'a on self as well, and that's probably not what you actually want.

To clarify, I meant the 'a in `Box<dyn Handler + 'a>` in the definition of `process_handler` is unnecessary. I'm not saying that the <'a> parameter in the definition of Handler::handle is unnecessary, which seems to be what you think I said, unless I misunderstood.


Ah yes, I misunderstood you in exactly that way, my apologies.

Lifetimes really only come into play if you are doing something really obscure. Often times when I’m about to add lifetimes to my code I re-think it and realize there is a better way to architect it that doesn’t involve them at all. They are a warning sign.

now show me an alternative syntax encoding the same information


There's a deeper connection there: lifetimes are a form of type variable, just like in OCaml.

While I don’t disagree that this is at first blush quite complex, using it as an example also obscures a few additional details that aren’t present in something like python, namely monads and lifetimes. I think in absence of these, this code is a bit easier to read. However, if you had prior exposure to these concepts, I think that this is more approachable. I guess what I’m getting at here is that rust doesn’t seem to be syntactic spaghetti as much as it is a confluence of several lesser-used concepts not typically used in other “simpler” languages.

Could have thrown a few uses of macros with the # and ! which threw me off completely while trying to read a Rust codebase as a non-Rust programmer.

> > really extreme use cases with lots of lifetime annotations and generic bounds

You choose as your example a pretty advanced use case.


Yeah, because if you exclude the bits that make Rust look like Perl then it won't look like Perl!

Which is the exact use case someone would choose rust for over other languages

No, the use cases of Rust are pretty much the same as the use cases of C++. Most Rust code shouldn't have objects with complicated lifetimes, just like most code in any language should avoid objects with complicated lifetimes.

That's simple even in Perl. The problem is when you start adding the expected idioms for real world problems.

sounds like something that could be solved with more learning

I wrote python for 15 years

I don't want to write python anymore. mainly rust if I can


> It seems to me that we ought to have a system-level language which builds upon the learnings of the past 20+ years

I mean, Rust does. I builds on 20+ years of compiler and type system advancements. Then Syntax is verbose if you include all then things you can possibly do. If you stick to the basics it's pretty similar to most other languages. Hell, I'd say a lot of syntax Rust is similar to type-hinted Python.

Having said that, comparing a GC'd dynamic language to a systems programming language just isn't a fair comparison. When you need to be concerned about memory allocation you just need more syntax.




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

Search: