Hacker News new | ask | show | jobs
by api 881 days ago
Rust indeed does have an embarrassing excess of diversity problem, made worse by the fact that async isn't in the standard library (outside bare minimum stuff like Future) and async libraries can't really be async runtime neutral. (This is going to improve with async traits.)

It's probably an inevitable result of Rust having a thinner standard library than Go, Java, C#, or other heavier languages. It's supposed to be systems language after all.

Another reason though is that Rust is a rich language with a very powerful type system, and rich powerful languages invite programmers to show off. One of the greatest things about Go is how boring it is. It doesn't give you a lot of room to show off by writing clever code, so instead you have to show off by making a great application. But some of this is unavoidable if you want what Rust delivers: a near-zero-overhead systems language with hard safety and automatic memory management without GC. That's dumping a heavy load on the type system, and Rust does deliver pretty well.

I try to exercise discipline when writing Rust and not show off by being more clever than I need to be. I do write some hand-whittled performance code here and there in high-performance applications, but I try really hard to avoid using unsafe. I've found that you usually can.

2 comments

> It doesn't give you a lot of room to show off by writing clever code, so instead you have to show off by making a great application.

I agree with your comment as a whole, but I have also seen plenty of dumb Go code. hey, lets toss out all compile time type checking:

https://johnstarich.com/go/pipe/pkg/github.com/johnstarich/g...

> by the fact that async isn't in the standard library

I wish async/await were not in the language

So just ignore the async use cases? Or force tokio or similar to support it?

My biggest pain point so far is channels, I like them in go because they are straight forward to use, allow concurrency, aren't a minefield of problems, and are easy to reason with. I wanted to scan N directories (in parallel) -> encrypt and checksum in (parallel) -> queue files for upload. With go channels it's straight forward.

I was looking for MPMC (like go channels) to allow multiple producers to enqueue and multiple consumers to dequeue. Rust doesn't do it and tokio doesn't do it. I could use crossbeam[0], flume[1], or async[2]. Not sure what limitations those have and what if any limitations in compatibilities with other crates I need will be.

[0] https://docs.rs/crossbeam/latest/crossbeam/channel/index.htm...

[1] https://docs.rs/flume/latest/flume/

[2] https://docs.rs/async-channel/latest/async_channel/

FWIW, either async-channel's types or flume's async APIs should work for your use case. Both are completely agnostic to the async runtime, since the futures are triggered by internal events (recv() waits for send() and vice versa), rather than external events like I/O which are generally coordinated through the runtime.
> So just ignore the async use cases? Or force tokio or similar to support it?

Hell no!

Do it differently

Differently how?

And perhaps more importantly, what tradeoff(s) would those differences entail?

Green threads/Virtual threads are one solution, but then you need to ship a runtime for them, then you pay for what you don't use.

Or don't standardize on them and have a split ecosystem of half libs supporting use `green-thread.rs` and the other half supporting `rust-gthreads`.

I wonder if you could have hidden green threads from the application entirely. Have a "full" standard library where std::thread is M:N green threads and I/O is implemented behind the scenes that way, and a "thin" version where std::thread is OS threads and I/O is just passed through to the OS. (Then of course no_std for embedded.)

Of course the problem then would be that Rust makes it trivially easy to call C code. As soon as someone in a green thread runtime called libc I/O functions directly they'd be in for a weird surprise when the call froze all threads on their worker.

Of course the "full" M:N runtime could be instrumented to print a warning when this occurs unless explicitly told not to, and std::thread in the "full" stdlib could allow explicit creation of full OS threads outside the runtime.

There is an attempt at doing this out there:

https://github.com/Xudong-Huang/may

> Green threads/Virtual threads are one solution, but then you need to ship a runtime for them, then you pay for what you don't use.

Indeed, and IIRC that's one of the reasons Rust removed its green thread implementation in the first place.

> Or don't standardize on them and have a split ecosystem of half libs supporting use `green-thread.rs` and the other half supporting `rust-gthreads`.

Don't forget the third lib for abstracting over the two!

However other people wished it was. So here we are.