Live data from Hacker News

Easy Mode Rust

llogiq.github.io

11–20 of 59 posts

Re: Easy Mode Rust

#11
Most of this stuff isn't addressing what makes Rust harder than, say, Go or C#. The pattern matching makes it if anything easier, since pattern matched dispatch is way easier to read than a bunch of complicated if/else blocks.

Rust is not garbage collected. It's a systems language, so it doesn't ship or run inside a big runtime. Rust's RAII and lifetime system does an admirable job of making most of the headaches and unsafe aspects of manual memory management fade into the background... until you throw in async.

After having used Rust for almost two years, I can confidently say that async and its interactions with memory are the hard part. (Edit: Making async code properly clean up after itself is also the hard part, unless you use a better async runtime than tokio.)

There are two paths with async. One is to use Arc everywhere, at which point Rust degrades into a more verbose version of Go but without automatic handling of circular references like you get with a good GC. You still get a thinner runtime and deterministic execution, which are benefits of a non-GC language at runtime, but you lose a lot of the efficiency and ergonomic benefits. You also end up with circular Arc references very easily if you have a task that owns two Arc classes that own tasks that own each other etc.

The second path with async is to use a scoped / structured concurrency library that lets tasks have lifetimes associated with them. Unfortunately those all come with not-entirely-safe warnings when used with tokio, and the underspecification of async in the standard library means the ecosystem has fixated on the runtime with nonexistent structured concurrency support (tokio) as the standard against which all other async runtimes link. You could use a superior async runtime like smol, but then you'd have to vendor/fork all your libraries.

TL;DR: async is still half-baked. The rest of Rust is actually easier than C++ and can be almost as easy as a language like C# once you grok lifetimes.

Edit:

IMHO tokio made a massive mistake in avoiding lifetimes associated with tasks and structured concurrency, probably on the speculation that this would be more confusing. It's not. It's more confusing not to have this, since you basically lose the entire lifetime system and its benefits in multithreaded async code. You also lose RAII in async code.

Rust also made a massive mistake in not more fully specifying async within the standard library and structuring it to be either runtime-included or runtime-agnostic. Until this happens async is half-baked and broken.

Re: Easy Mode Rust

#12
post #7

Earlier quoted context omitted.

Rust can: - Do the things C and C++ can do. - Without the memory corruption issues those languages are infamous for. - With the conveniences you'd expect of any post-internet language. (A library ecosystem that's unified around a standard build system and package manager, an async IO story, UTF-8 strings, etc.)

Annoyingly said library ecosystem with the standard build system and package manager becomes a pain to deal with when you're trying to do something like add packages made in the language to distros, requiring a bunch of hacks to do things like just have Cargo not try to reach online to get all the dependencies. Also stuff like the way feature flags are used cause a combinatorial explosion of packages just so you can…

I don't really know what distro package managers are offering here.

Re: Easy Mode Rust

#13
The self-modifying code mentioned in the Macros section could be automated in an IDE! That is, your IDE should be able to show you the expansion of a macro at that specific place. This feature would help me much more to understand what's going on than navigation to the macro source.

I'm not sure if there are any IDEs supporting (temporary) macro expansion already. The output showing all macros expanded is usually too verbose.

Re: Easy Mode Rust

#14
post #11

Most of this stuff isn't addressing what makes Rust harder than, say, Go or C#. The pattern matching makes it if anything easier, since pattern matched dispatch is way easier to read than a bunch of complicated if/else blocks. Rust is not garbage collected. It's a systems language, so it doesn't ship or run inside a big runtime. Rust's RAII and lifetime system does an admirable job of making most of the headaches and…

I find this very interesting:

> [...], I can confidently say that async and its interactions with memory are the hard part. (Edit: Making async code properly clean up after itself is also the hard part, unless you use a better async runtime than tokio.)

I would love to read more about this, are there any specific areas or things I might search for?

Re: Easy Mode Rust

#16
I was looking at what Mojo is doing for the first time in half a year or so the other day. It seems to be coming along nicely as an "easier to use Rust with first class Python and MLIR interop". At first glance, it looks like what this article is proposing is rather similar in spirit. E.g. in Mojo when a function wants ownership, but the caller doesn't give it, the value automatically gets copied without requiring additional annotations. The annotation is needed for transferring ownership instead, etc...

https://docs.modular.com/mojo/manual/values/

Re: Easy Mode Rust

#18

Cool cheatsheet. As someone new to Rust, what are the benefits versus Go, C++, and C?

A sibling comment already mentioned the type system as a whole, but I wish to highlight one specific feature: Rust has algebraic data types.

The term sounds academical, but I honestly can't see a modern, programmer-friendly language not having proper discriminated union types in 2024. Go's lack of sum types is not simplicity. It's a glaring omission, forcing programmers to rely on "idioms" like using tuples to return errors. Having only product types (structs) is literally like trying to do arithmetic with only multiplication, without addition.

Re: Easy Mode Rust

#19

Cool cheatsheet. As someone new to Rust, what are the benefits versus Go, C++, and C?

I don’t know Go, compared to C and C++:

* safe by default — for example all array accesses are checked by default. You can do unchecked access for speed when you need, and you can check for safety in C and C++ if you want, but I feel nowadays we really should be using “safe by default”.

* much easier to parallelize. I had basically given up on multithreading in C++ and believed it was almost impossible to do well. In Rust, in my experience, if parallel code compiles it works correctly. This is because the borrow checker stops you ever mutating anything in two threads at once at compile time.

Re: Easy Mode Rust

#20
post #13

The self-modifying code mentioned in the Macros section could be automated in an IDE! That is, your IDE should be able to show you the expansion of a macro at that specific place. This feature would help me much more to understand what's going on than navigation to the macro source. I'm not sure if there are any IDEs supporting (temporary) macro expansion already. The output showing all macros expanded is usually too…

Rustrover (IntelliJ) can do some of this. You can use quick actions to expand a macro inline in the IDE.
Post reply on HN