Hacker News new | ask | show | jobs
by bornfreddy 1033 days ago
This might not be a popular opinion, but - I love the idea of Rust, but the language itself seems needlessly complex to me. It looks like the authors tried to cover so many esoteric usecases that the result is more like C++ than C (which is not good in my eyes).

One example:

> `let` patterns can be used as conditions in `if`:

Anyway, this tutorial looks great, I'll give Rust another go when I have a good project for it, but to me it seems there is a great need for a simpler language with similar memory management. Rust is just too complex, imho.

5 comments

> > `let` patterns can be used as conditions in `if`:

You can code without that, it's just that you are going to wish you had it after your first few programs.

> but the language itself seems needlessly complex to me

A lot of the complexity is around making the explicitness of the language bearable. If you had to be explicit without all the syntax sugar and type / lifetime inference it would be insufferable to program in.

Not sure if I disagree but your example is terrible. Let patterns in if statements greatly simplify things. The alternatives are a lot more convoluted.

See example from code I wrote this week: https://github.com/trane-project/trane/blob/master/src/data/...

Without the let, I'd have to do a match statement followed by an unwrap just to check if a field in an enum is set.

You are confusing shortening with simplifying. The `if let` syntax is an unnecessary addition to the language so it is by definition an added complexity. Of course the resulting code is much easier to read and I definitely agree that it's a nice feature but I wouldn't pretend it made the language simpler, just more comfortable once you already know it.
You imply simple = better, complex = worse, but this isn't such a simple relationship.

If you keep removing redundant constructs from languages, you will end up with something pure and minimal like the lambda calculus or turing machine. But these aren't easy to program in!

Languages are an interface for humans. If the code density is too low or too high, it becomes difficult to for people reason about the programs. Concepts like readability and expressiveness are important, but end up requiring some level of complexity.

Exactly, it's a smooth trade off curve from lambda calculus all the way to Haskell with 30 language extensions enabled.

You could learn lambda calculus in an afternoon, but then spend a month writing a single complex algorithm. You could spend 5 years learning Haskell, and then write the most powerful system in a couple minutes.

There's a sweet spot somewhere between those two, and Rust is definitely near it.

Exactly what I had in mind, thank you for explaining it so succinctly!
Just to elaborate on the "just more comfortable once you already know it" part, I think a lot of Rust is optimized (willingly or not) for the "once you know it" use case.

Which, while it might arguably hurt adoption, is a good value proposition, since you spend a lot more time knowing the language than not knowing it.

Now, not everything is always perfect, and I agree that the `if let` is not the most useful part of the language, as it drives pointless discussions about when to use it vs match (some people prefer the esthetics of match even when an if let can be used, others prefer to use a if let whenever possible). This redundancy apart, the construct doesn't eat any mental energy once you know it. The same can't be said of many of C++ quirks (initialization rules, member-initializer list in constructors, the rule of five)

> Without the let, I'd have to do a match statement followed by an unwrap just to check if a field in an enum is set.

You... would not?

An `if let` trivially desugars to a `match`.

    if let <pat> = <expr> {
        ...
    }
becomes

    match <expr> {
        <pat> => {
            ...
        }
        _ => {}
    }
See my example. I am trying to extract the value of an optional field in the enum. That's where the unwrap comes in.
What I wrote up is the spec-defined desugaring of a single-branch `if let`, aside from desugaring bugs there should be no counter-example.

The snippet you link to should be replaceable by something along the lines of

    match &passages.asset {
        TranscriptionAsset::Track {
            artist_name: Some(artist_name),
            ..
        } => {
            metadata
                .entry(ARTIST_METADATA.to_string())
                .or_default()
                .push(artist_name.clone());
        }
        _ => {}
    }
no `unwrap` needed. I don't know where you got that idea.
> I love the idea of Rust, but the language itself seems needlessly complex

Well, I have to agree Rust isn't one of the simplest PL-s on the planet. This is due to the fact that it is quite a modern PL and quite a versatile PL, supporting elements of functional programming, trait-oriented (conditional generics) programming, asynchronous programming, etc. and a capable standard library on top of it all. It takes, indeed, quite some time to take all of that Rust in. As a reward, you get a lot of expressiveness and the capability to discover a significant percentage (if not an overwhelming majority) of your programming mistakes at compile time as opposed to runtime (unit/integration tests or Q/A), which to me is priceless.

Nevertheless, from my experience, Rust is, at the same time, one of the most... "consistent", "predictable", "internally symmetric" PL-s I have ever seen. I consider Rust to be way easier to learn than, say, Swift.

> `let` patterns can be used as conditions in `if`

I somewhat cannot help it but feel that such an example should not be used in an introductory tutorial. `if let` is usually used to pattern-match a simple `enum`[1], not a complex `struct` that looks "dense" unless you're used to it.

[1] https://doc.rust-lang.org/book/ch06-03-if-let.html

> ...and quite a versatile PL, supporting elements of functional programming, trait-oriented (conditional generics) programming, asynchronous programming, etc. and a capable standard library on top of it all.

Yes, exactly! Now can I have just a safe C without all the other stuff, pretty please? :) I understand Rust is not it, but I hope someone comes up with a simple PL which is also compile-time memory safe. I think it would be an instant hit.

> Now can I have just a safe C without all the other stuff, pretty please? :)

Not sure how much we could remove from Rust while keeping the problem tractable. The borrow checker is needed for compile time memory safety sans garbage collection. For borrow checking to be tractable, one needs shared references, exclusive references, owned values, the possibility for reference counting, and the possibility for interior mutability. This mandates smart pointers, that pretty much mandate generics. These various abstractions also mandate a capable standard library, unless the language would hardcode all these abstractions, which would endanger the low-levelness of the language (since you wouldn't be able to implement your own abstractions for e.g. embedded contexts)

For the demarcation between unsafe Rust and safe Rust to work, one needs encapsulation, so field privacy.

For concurrency, the language requires a way to mark types as thread safe, which requires a way to say things about structures, so a least a weak version of traits.

A smaller version of Rust probably exists, but I don't think we could remove as much of the language as we could imagine at first without compromising safety. It also wouldn't be "just a safe C", because C itself has heaps of accidental complexity (e.g. the way function pointers are declared, integer promotions, implicit conversions, the antiquated textual inclusion compilation model that is prone to ODR violations and complicates build systems, array decay, and so on...) that would need to be removed.

> Now can I have just a safe C without all the other stuff, pretty please? :)

Hmmmmm, if you are not being forced by others into any particular Rust feature set (standard) and you are not being forced into any preexisting Rust codebase, then perhaps just forget the entire standard library, traits, async, etc. and go Bare Metal Rust[1], using only the Rust language features that you need and interfacing with external C APIs through Foreign Function Interface[2].

[1] https://google.github.io/comprehensive-rust/bare-metal.html

[2] https://doc.rust-lang.org/nomicon/ffi.html

The problem with Rust is learning it for...

> when I have a good project for it

... is a bad idea. I've heard this from several programmers who now use Rust professionally, but don't have first-hand experience about it. Their take can be boiled down to:

Rust has a learning curve that will mean that the first version will invariably be throw-away. Developing a mental model for how to change memory management structures to please the borrow checker and properly encode ownership seems like the biggest hurdle to writing good Rust.

I believe this is the main reason Rust adoption isn't skyrocketing, but still growing. The "onboarding" for Rust is more labor intensive and therefore disincentivizes its use.

I understand it will be a throwaway project, that's fine. But it helps me learn if I have a goal in front of me.
> One example:

Your example is a minor but quite useful piece of syntactic sugar, which was introduced a few years in following a similar construct being successful in Swift. You can read the entire reasoning at https://rust-lang.github.io/rfcs/0160-if-let.html

I agree with the GP. "if let" is just such a weird contruct that it doesn't feel justified to be in the language. it just screams of over engineering. now "let else" is cool.