Rust에 대한 Future 시작

최근에는 Rust의 퓨처와 async/await 사용법을 조사해 미리 적었다.

첫 번째 예


우선 HTTP 서버에 출력 응답을 요청하는 프로그램을 써 보십시오.
// [dependencies]
// tokio = { version = "1.9", features = ["full"] }
// reqwest = "0.11"

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

#[tokio::main]
async fn main() -> Result<()> {
    let resp = reqwest::get("https://httpbin.org/get").await?;
    let body = resp.text().await?;
    println!("body = {}", body);
    Ok(())
}
reqwest::get()는 지정된 URL에 HTTP 요청을 보내는 함수입니다.반환치는 Future에 소포됩니다.
퓨처.await에 작용하면 퓨처가 완성되면 결과를 얻을 때까지 기다릴 수 있다.이것.await은 문법적으로는 방법처럼 보이지만 방법이 아니라 전용 언어 기능이다.
main 함수는 일반 fn 이 아니라 async fn 의 가이드가 있음을 주의하십시오.

async 함수


다음은 첫 번째 예부터 함수를 잘라냅니다.결과는 다음과 같다.
// [dependencies]
// tokio = { version = "1.9", features = ["full"] }
// reqwest = "0.11"

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

async fn http_get(url: &str) -> Result<String> {
    let resp = reqwest::get(url).await?;
    let body = resp.text().await?;
    Ok(body)
}

#[tokio::main]
async fn main() -> Result<()> {
    let body = http_get("https://httpbin.org/get").await?;
    println!("body = {}", body);
    Ok(())
}
주의해야 할 것은 발표#[tokio::main]할 때http_get의 문법을 사용했다는 것이다.함수를 async fn로 선언하면 구문async fn을 사용할 수 있습니다..await는 async 함수이기 때문에 일반적인 값을 되돌려주지 않고 http_get에 포함된 값을 되돌려줍니다.따라서main 함수는 Future의 반환값http_get을 얻기 위한 것이다.

병렬 실행 (하나) 오류 예시


모처럼 비동기적으로 수행했기 때문에 HTTP 요청을 병렬로 보내세요.
먼저 잘못된 예를 소개하다.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

// 前の例と同じなので省略
async fn http_get(url: &str) -> Result<String> {
    ...
}

#[tokio::main]
async fn main() -> Result<()> {
    // まず Future をふたつ作る
    let fut1 = http_get("https://httpbin.org/delay/5");
    let fut2 = http_get("https://httpbin.org/delay/5");

    // そのあと await で待つ
    let body1 = fut1.await?;
    let body2 = fut2.await?;

    println!("body1 = {}", body1);
    println!("body2 = {}", body2);
    Ok(())
}
본 예에서 사용한 .await의 종점은 5초를 기다린 후에 응답의 종점으로 돌아간다.만약 정확하게 병행 집행할 수 있다면 이 프로그램은 5초 정도 지나면 끝난다.하지만 이 종목은 10초가 걸린다.
Rust의 Future는 실행되지 않도록 제작되었습니다.즉, 호출https://httpbin.org/delay/5하더라도 Future를 작성할 뿐 실행하지 않는다는 것이다.다른 언어에 익숙한 퓨처와 프로미스의 사람들이 쉽게 빠져드는 함정이라는 점에 유의하시기 바랍니다.Rust의 Future는 무선 중에만 실행됩니다.
Rust의 Future가 이런 디자인이 된 것은 효율을 위해서다.관심 있는 사람이 읽는 것이 좋다Designing futures for Rust.

병렬 실행 (2) 정확한 예시


병렬 실행을 원할 때 futures cratehttp_get() 함수를 사용합니다.
// [dependencies]
// tokio = { version = "1.9", features = ["full"] }
// reqwest = "0.11"
// futures = "0.3"  # 追加!

use futures::future; // 追加!

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

// 前の例と同じなので省略
async fn http_get(url: &str) -> Result<String> {
    ...
}

#[tokio::main]
async fn main() -> Result<()> {
    let fut1 = http_get("https://httpbin.org/delay/5");
    let fut2 = http_get("https://httpbin.org/delay/5");

    // join を使ってふたつの Future を同時に待つ
    let (result_body1, result_body2) = future::join(fut1, fut2).await;

    // http_get の戻り値は Result に包まれているので、Result を外す
    let (body1, body2) = (result_body1?, result_body2?);

    println!("body1 = {}", body1);
    println!("body2 = {}", body2);
    Ok(())
}
이 예는 병행 집행된 것이기 때문에 5초 정도에 끝난다.이 예에 나타난 함수 join 는 두 개의Future를 수신하고 그것들을 병행하여 실행하는 새Future를 되돌려 주는 함수이다.
Join과 Result를 연속으로 뜯어내는 경우가 많기 때문에 join 이 함수를 준비했습니다.이것을 사용하면 위의 예는 다음과 같은 내용을 쓸 수 있다.
let (body1, body2) = future::try_join(fut1, fut2).await?;
try_join의 매크로를 준비했다.join!는 가변 길이 매개 변수로 몇 개의Future가 있든지 간에 Join을 일격으로 칠 수 있다.
let (ret1, ret2, ret3, ret4) = futures::join!(fut1, fut2, fut3, fut4);
join!join!달리join까지만 진행됩니다. 주의하세요.또 await에 대응하는 매크로try_join를 준비했다.

많은 수의 Future 동시 실행


나는 두 개의 퓨처를 합성하는 방법을 이해했다.그럼 더 많은 퓨처링을 하고 싶을 때는 어떻게 해야 하나요?
이 경우 try_join! 함수를 사용할 수 있습니다.다음 예는 Future 1000개를 만들어 병행한 예입니다.
// [dependencies]
// tokio = { version = "1.9", features = ["full"] }
// futures = "0.3"

use futures::future;
use std::time::Duration;

// 5秒待って引数をそのまま返す非同期関数
async fn some_heavy_work(id: i64) -> i64 {
    tokio::time::sleep(Duration::from_secs(5)).await;
    id
}

#[tokio::main]
async fn main() {
    // 1000個の Future を作る (このタイミングでは実行されていない)
    let works: Vec<_> = (0..1000).map(|i| some_heavy_work(i)).collect();
    // 1000個の Future を並列実行する
    let ret = future::join_all(works).await;

    println!("ret = {:?}", ret);
}
join_all에 대응하는 함수try_join도 있다.
보다 복잡한 제어가 필요한 경우 FuturesOrdered 또는 FuturesUnordered를 사용할 수 있습니다.