Rust : async

De Justine's wiki
Aller à la navigation Aller à la recherche

Exemple simple

Cargo.toml:

[dependencies]
futures = { version = "0.3.*" }
tokio = {version = "0.2.*", features = ["full"] }

Explications dans du code:

<source lang=rust>

use futures::prelude::*; use tokio::prelude::*; use tokio::task;

//The principle of asynchronous programming is to get out of the usual way of programming, //where the program executes from start to finish, blocking on each task. If we have a web server //for example, we may need to answer 10k requests at the same time. //We could use threads, and Rust's fearless concurrency; but asynchronous programming is another //way of doing things. // //So, we have asynchronous functions, that we can launch. They return a handler, and we can keep on //executing code, coming back to the handler to see if it yields anything. Only in these //asynchronous function can we use asynchronous things. // //In Rust, we need two things to do that : the "futures" crate, and a Runtime since Rust does not //come with it by default. The most common on is Tokio, which we'll use here. // //We can then do 3 basic operations : start the runtime, spawn a future, and spawn CPU-blocking //intensive operations. // //Of course, most work will happen in futures, this way. We need to be able to offset the execution //of cpu-intensive things to other threads so we don't block at some point in our main thread, otherwise it is all //useless.

type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;


//1 - Starting the runtime. This is the shortest version, using a trait

  1. [tokio::main]

async fn main() {

   //2.2 Here we finally get the result of it all.
   app().await.unwrap();
   //3.2 Here we execute our CPU-intensive future.
   otherapp().await.unwrap();

}

//1 bis - This is a more complicated way of doing the same: //start the runtime, spawn a future, and block on it to get an answer. fn othermain() {

   //runtime
   let mut rt = tokio::runtime::Runtime::new().unwrap();
   //future
   let future = app();
   //blocking
   rt.block_on(future);

}

async fn our_async_program() {

   println!("Hello world");

}

//2.1 - Here we spawn a future. //We spawn it using 'task' so we can have multiple futures running at once. //Note that this function is asynchronous, too. async fn app() -> Result<()> {

   let join = task::spawn(our_async_program());
   let res = join.await?;
   Ok(())

}

//3.1 - Spawning CPU-intensive tasks.

//This fn is CPU-intensive. fn fib_cpu_intensive(n: u32) -> u32 {

   match n {
       0 => 0,
       1 => 1,
       n => fib_cpu_intensive(n - 1) + fib_cpu_intensive(n - 2),
   }

}

//So we spawn a blocking future for it. //Note that this is also asynchronous. async fn otherapp() -> Result<()>{

   let threadpool_future = task::spawn_blocking(||fib_cpu_intensive(30));
   //And await it.
   threadpool_future.await?;
   Ok(())

} </source>