DEV Community

Discussion on: My first impressions of Rust

Collapse
 
mmstick profile image
Michael Murphy • Edited

Concurrency in Rust is not tied to a particular threading module, and may not even be tied to threads at all. It is up to the programmer to choose how to concurrently execute their tasks.

Using futures, it is possible to join multiple futures together so that they execute concurrently from the same thread. When one future is blocked, the next future in the queue will be executed.

// Concurrently execute two tasks on the current thread
let (result1, result2) = join!(future1, future2);

// Concurrently execute tasks with a common error type.
let result =  try_join!(future1, future2, future3);

It's also possible to distribute them across a M:N thread pool, and interchangeably mix and match both approaches. Spawning a future will schedule it for execution on your executor. The executor may be based on a thread pool, or run on the same thread. Depending on which you choose, they may have Sync / Send restrictions.

task::block_on(async move {
    // Each spawn returns a JoinHandle future to the result.
    let future1 = task::spawn(future1);
    let future2 = task::spawn(future2);
    let future3 = task::spawn(future3);

    // Concurrently wait for all three threads to complete.
    let (result1, result2, result3) = join!(future1, future2, future3);
});

Often times there's two different pools to spawn tasks to: non-blocking thread pools, and blocking thread pools. Tasks which block should be spawned on blocking pools so that they avoid blocking tasks on the non-blocking thread pool. The async-std crate provides spawn_blocking for that.

Without futures, you may use crates like rayon to distribute blocking tasks across a thread pool. This used to be the preferred threading model before the introduction of async/await with futures.


There is an issue with your assertion that functions aren't first class. You can accept both functions and closures as input arguments, and return functions and closures as output arguments. Generics is required for returning closures, however, because closures are dynamically-created types.

fn function_returning_function() -> fn()  {
    name_of_other_function
}

fn function_returning_closure() -> impl Fn() {
    let mut var1 = ...;
    let mut var2 = ...;
    move || {
        ...
    }
}
Collapse
 
deepu105 profile image
Deepu K Sasidharan

Yes, concurrency in Rust is quite interesting