From what I've asked people, the complaints fall mainly into two categories:
* familiarity and aesthetics. Rust uses fewer round () parens, and more <> and {}. This makes it look alien, and people say it's unreadable when they don't immediately recognize the language constructs they know.
* misattributing difficulty of learning Rust concepts like lifetimes and generics to their syntax. People say they want less syntax, but they mean they don't want to explicitly specify things like ownership and trait bounds.
The more interesting follow up question is: "How would you express the same semantics with a different syntax?" Too often discussions about Rust's ugly syntax lack this crucial component.
Is it? I don't know if keeping Rust's syntax C++-like is a stated goal of the project, but okay sure we can add that caveat to my question.
The big problem I see is that people will say "Lifetime annotations are ugly and noisy" and when pressed on the issue will eventually concede that they just want GC(which is not about syntax, but semantics)
It was a goal, though lower priority than other goals. You can see this in the way Rust is largely “curly braces and semicolons” but at times when there’s good reasons it diverged from that, like the ML style let syntax.
> I don't know if keeping Rust's syntax C++-like is a stated goal of the project
They won't change the syntax, the whole discussion is hypothetically anyway.
What I wanted to express is that most people want a C++-style syntax (of generics), even if they complain about Rust's syntax.
I say this as a Rust enjoyer, but I think what most people mean when they say this is that there's a lot going on, especially in the function definition syntax. When you start adding in lifetimes and generics with bounds, async, &muts, where clauses... it really does become unreadable. I don't really see a way to fix this without making the syntax even more verbose, or aggressively simplifying the type system to the point that function argument inference becomes viable, but then you might end up in a situation like Swift's where type checking a simple expression takes an excessively long time.
That and the colon-colons (::), probably. Those can add a lot of noise.
< Andys> oh dear
< Andys> in ruby, symbols are represented with a prepended colon
< Andys> eg. :flag
< Andys> so some guy tshirt that said ":sex"
< Andys> which everyone at railscamp knew meant "Sex symbol"
< Andys> he wore it until someone pointed out that to non-rubyists it said "Colon sex"
I think it's not that much the specific parts as it is the combination of (sometimes obscure) symbols forming dense blocks of sigils that you have to carefully pick apart to understand what the code is doing. It's easier to reason about code if it uses words instead of symbols, and Rust seems to desperately want to avoid using actual words (I don't count "fn" as a word, and "pub" is a word, but for me it's somewhere where you go to have a drink after work). Ok, I know there is a long tradition of doing this in C-family languages, but Rust has driven it to new heights, and it has a lot more concepts (with their attached symbols) that you have to keep in your head.
I hate the unpronounceable sigils of languages like Haskell, but C-style abbreviations don't really bother me somehow. The abbreviations help you remember what the words are, at least, and for me this seems to be enough.
Here's my litmus test: read the source aloud. Over the phone to a person who doesn't see your screen, if needed. Did you have an obvious, understandable, and simple pronunciation for everything, that wasn't just reading ASCII characters one by one?
Now pretend the other person is a smart and experienced programmer, but has never heard of Rust. How would they write down what you just told them, without any idea of Rust's syntax? That's the non-ugly version of Rust.
Limiting examples to a subset of Rust syntax will produce more readable answers, sure. You have no lifetimes and no borrows, and very limited generics.
The information density of the "human syntax" Rust syntax both remain linear in size, but with a significant different N.
function max
with
a lifetime a,
a lifetime b,
and type parameter T
with
argument a of a reference to type T that lives as long as lifetime a
argument b of a reference to type T that lives as long as lifetime b
and returns a value of type parameter T
where T implements trait PartialEq
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, A: Allocator> IntoIterator for &'a Vec<T, A> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
An implementation of trait IntoIterator
(that has been stable since Rust 1.0.0)
with a lifetime a
a type parameter T
a type parameter A which implements the trait Allocator
for a borrow for the duration of lifetime a of type Vec with type parameters T and A
it has an associated type Item, which is a borrow for the duration of lifetime a of type T
it has an associated type IntoIter, which is type Iter from module slice with parameters lifetime a and type T
it has a method into_iter
that consumes the receiver
and returns associated type IntoIter
the methods body
calls method iter on the receiver
and returns its resulting value
I'm not arguing that complex things will somehow become non-complex.
However, none of the human syntax of Rust includes things like :: or '. To a first approximation[1], those are the parts that people can experience as "ugly", and they are not present in the human syntax. This is what people mean when they say "sigil heavy" or "punctuation based" syntax -- things that are generally are not read out as such. This is the space where you can make arguments about beauty.
[1]: Only roughly so because your chosen human syntax still encodes some Rust syntax decisions like predeclaring generic lifetimes and types, and using "where" instead of an inline clause. Those parts of syntax can also be shuffled around for subjective values of "not ugly".
For what is worth, the turbo fish falls from the decision to use <> for generics (for familiarity for C++ and Java developers) and a desire to make the syntax non-ambiguous (side stepping the C++ problem of having to delay determining if something is a type path or a comparison, mixing parsing and name resolution).
I keep hearing this from decent chunks of people who don't write rust, but the language doesn't seem that far off C to me. It's certainly no haskell.