Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

There's a few things here that make this hard in Rust:

First, the main controller may panic and die, leaving all those tasks still running; while they run, they still access the two local variables, `number` and `delay`, which are now out of scope. My best understanding is that this doesn't result in undefined behavior in C#, but it's going to be some sort of crash with unpredictable results.

I think the expectation is that tasks use all cores, so the tasks also have to be essentially Send + 'static, which kinda complicates everything in Rust. Some sort of scoped spawning would help, but that doesn't seem to be part of core Tokio.

In C#, the number variable is a simple integer, and while updating it is done safely, there's nothing that forces the programmer to use Interlocked.Read or anything like that. So the value is going to be potentially stale. In Rust, it has to be declared atomic at the start.

Despite the `await delay`, there's nothing that awaits the tasks to finish; that counter is going to continue incrementing for a while even after `await delay`, and if its value is fetched multiple times in the main task, it's going to give different results.

In C#, the increment is done in Acquire-Release mode. Given nothing waits for tasks to complete, perhaps I'd be happy with Relaxed increments and reads.

So in conclusion: I agree, but I think you're arguing against Async Rust, rather than Rust. If so, that's fair. It's pretty much universally agreed that Async Rust is difficult and not very ergonomic right now.

On the other hand, I'm happy Rust forced me to go through the issues, and now I understand the potential pitfalls and performance implications a C#-like solution would have.

Does this lead to the decision fatigue you mention in another sub-thread? It seems like it would, so I'll give you that.



For posterity, here's the Rust version I arrived at:

    let number = Arc::new(AtomicUsize::new(0));
    let finished = Arc::new(AtomicBool::new(false));

    let finished_clone = Arc::clone(&finished);
    let delay = task::spawn(async move {
        sleep(Duration::from_secs(1)).await;
        finished_clone.store(true, Ordering::Release);
    });

    for _ in 0..10 {
        let number_clone = Arc::clone(&number);
        let finished_clone = Arc::clone(&finished);

        task::spawn(async move {
            while !finished_clone.load(Ordering::Acquire) {
                number_clone.fetch_add(1, Ordering::SeqCst);
                task::yield_now().await;
            }
        });
    }
    
    delay.await.unwrap();
https://play.rust-lang.org/?version=stable&mode=debug&editio...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: