this post was submitted on 08 Sep 2023
44 points (90.7% liked)

Rust

6128 readers
27 users here now

Welcome to the Rust community! This is a place to discuss about the Rust programming language.

Wormhole

[email protected]

Credits

  • The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)

founded 2 years ago
MODERATORS
top 24 comments
sorted by: hot top controversial new old
[–] pileghoff 24 points 1 year ago* (last edited 1 year ago)

Async rust might suck, compared to async in higher level languages, but for someone comming from C, async rust simplifies a lot of stuff. It often feels like a lot of criticisms of rust boils down to the fact that rist was sold to both people using low and high level languages. I don't doubt that async rust is shit when all you want is a faster typescript.

Edit: I certainly also have my criticisms of rust and its async implementation, and I think some of the authors concerns are valid, it was just an observation about the tension between the needs of the two groups of users.

[–] BB_C 23 points 1 year ago* (last edited 1 year ago) (1 children)

fn foo(&big, &chungus)

is out,

async fn foo(&BIG_GLOBAL_STATIC_REF_OR_SIMILAR_HORROR, sendable_chungus.clone())

is in.

Or maybe you know

fn foo(&big, &chungus)

is out

async fn foo(big, chungus) -> (big, chungus)

is in

Or

async fn foo(big, chungus) {
  // ...
  tx.send((big, chungus)).await?;
  // ...
}

is in

Moving (movable/sendable) data is not limited by number or direction, you know. And that second one even makes use of them great Hoare channels! And gives us control on how long we hold on to data before sending it back (modified or not). But I digress. Let's go back to the important talking point that Hoare was right!

[–] BatmanAoD 1 points 1 year ago (1 children)

I think the point of the "BIG_GLOBAL_STATIC..." name is that global statics are bad, not that the syntax is ugly. That said, you're absolutely correct that combining channels with async code is the way to go.

[–] BB_C 0 points 1 year ago

I think the point of the “BIG_GLOBAL_STATIC…” name is that global statics are bad, not that the syntax is ugly.

Yes. And my point was that there is an obvious way of sharing data besides passing static-refs, cloning, and using Arcs, which is moving data bidirectionally. That was conveniently, or ignorantly, glossed over by the coping gopher.

[–] [email protected] 16 points 1 year ago* (last edited 1 year ago) (1 children)

Interesting read but I don't agree that it's as bad as the author makes it sound. I'm also curious what an alternative would be, if you don't want a garbage collector?

In my personal experience, you don't run into all the Arc, Pin and 'static stuff that often. I would even say very rarely.

[–] [email protected] 3 points 1 year ago

I agree, I've written a lot of async rust and it's rarely an issue for me. I have more issues with the generated futures and the traits they implement not matching what I need, meaning I often have to jump to manually created futures and pin-project (which isn't too bad tbh but far more work than writing with async/await).

[–] [email protected] 13 points 1 year ago (1 children)

Maybe it's just me, but isn't async programming a mess in all programming languages?

[–] noli 1 points 1 year ago (3 children)

It's a joy to do async in go IMO

[–] [email protected] 7 points 1 year ago* (last edited 1 year ago)

not really. first of all async in not the same as threading. And even then, while it makes parallel code easier to write (not easier to reason about), it still has the exact same footguns as anything else, as soon as you venture away from having only one consumer for every producer. Synchronization is still all on you

[–] [email protected] 6 points 1 year ago (1 children)

That's a whole different thing to me. That's not async, that's channels and multithreading.

I do that in Rust as well with mcsp channels and it's been fine.

It's the async/await bit that I find incredibly akward all the time.

[–] [email protected] 4 points 1 year ago (1 children)

Channels and multithreading are a solution to async problems. Instead of a keyword trying to abstract away the async, you use a mechanism for communicating between coroutines. You can run Go with a single execution thread and still get benefits from goroutines and channels. In fact, Go didn't turn on multithreading until 1.5.

Go solves async with goroutines and channels, not with an async keyword. The runtime is pretty heavy and steps in when standard library functions would block. In other words, it's async by default since blocking IO causes another goroutines to execute.

[–] [email protected] 0 points 1 year ago (1 children)

@sugar_in_your_tea @wim
go channels and goroutines are very good and easy to work, but thei cant acquire the performance and security of #tokio. You can write good code and solutions with goroutines, but there are limitations. #Rust async is a bit more difficult to do, but its not so or too complicated or dificult, and you will choose between the two languages by kind of problems you want to solve.

[–] [email protected] 1 points 1 year ago (1 children)

Yes, there's absolutely a lot of good reasons to use Rust over Go, even for heavily async tasks, I'm merely saying that Go supporting channels in the language makes it a lot easier to use for async tasks. There's one proper way to send data between concurrent contexts, and that's a channel, so it gets used a ton in library code.

Rust could get a lot of that benefit by including channels in the standard library. We could still keep the async reactor code out of the standard library, but we'd need trait definitions there so the channels could hook into them.

I personally think the Rust standard library should ship a complete async solution, with core bits being overridable (like with memory allocation), which would make it a lot easier to write clean async logic. I think the standard library should be single threaded, but be multi-threaded compatible, and then allow third party libraries to provide the multi-threaded capability.

[–] [email protected] 0 points 1 year ago* (last edited 1 year ago) (1 children)

@sugar_in_your_tea #golang is a near perfect aproach for writing concurrency and async code, indeed, but rust already has channels in standard library. My github has a lot of concurrency code using only std library, including examples in atomics, channels, mutexes, conditional variables, etc...

https://doc.rust-lang.org/rust-by-example/std_misc/channels.html

https://github.com/jcbritobr/concprog/blob/master/src/threadpool.rs

https://github.com/jcbritobr/concprog/blob/master/src/channels.rs

[–] [email protected] 1 points 1 year ago

Huh, it has been a while since I did async in Rust. I used Actix to build a multi-protocol game server for a toy project, and the only state staring needed went through the database.

I'll have to play with async Rust some more. I've looked through a lot of async code, and while it looks gross, I haven't actually written much myself to really get a feel for the ergonomics. For other projects, I've just used threads and mutexes, which has been plenty. The closest I've gotten was messing with GUIs, but that's been mostly GTK or IMGUI, which have their own synchronization patterns.

So maybe it's good enough as is.

[–] [email protected] 1 points 1 year ago (1 children)

To be fair, a lot of that is because the scheduler detects blocking IO and context switches.

Rust could get really far with Go-style channels.

[–] [email protected] 1 points 1 year ago (1 children)

Are Go-style channels different from what Tokio provides? https://tokio.rs/tokio/tutorial/channels

[–] [email protected] 4 points 1 year ago

They're very similar, but with very different ergonomics. Go channels are part of the language, so libraries use them frequently, whereas tokio is a separate library and not nearly as ubiquitous. So you'll get stuff like this:

c := make(chan bool)
go func () {
    time.Sleep(time.Second*2)
    c <- true
} ()

select {
case val := <-c:
case _ := <-time.After(time.Second)
}

This lets you implement a simple timeout for a channel read. So the barrier to using them is really low, so they get used a ton.

I haven't looked at the implementation of tokio channels, so I don't know if there's something subtly different, but they do have the same high level functionality.

[–] acow 11 points 1 year ago

It really is interesting how async Rust takes the shine off of Rust to such an extent. If good old stack based, single threaded Rust wasn’t so polished, I don’t think the async parts would stand out so much. Something that might help is to have some sort of benchmark showing that Arcing through an async problem is still faster than typical GCed languages.

[–] [email protected] 4 points 1 year ago
[–] [email protected] -5 points 1 year ago (3 children)
[–] [email protected] 4 points 1 year ago

Mf's have no sense of humor here..

Or.. at least MY sense of humor.

[–] BatmanAoD 4 points 1 year ago

Zig's approach seems even more low-level and manual: https://ziglearn.org/chapter-5/

(In general, I think Rust and Zig both seem valuable, and I think it's a mistake to treat programming language success as a zero-sum game.)

[–] [email protected] 1 points 1 year ago

Also, my meme was based on https://kristoff.it/blog/zig-colorblind-async-await/ I didn't randomly pick zig.