RustでFutureなクロージャを関数の引数にしてみる


目的

RustでFutureなクロージャを関数の引数にしてみる。

コード

main.rs
use std::future::Future;

async fn example<Fut>(f: impl FnOnce(i32, i32) -> Fut) -> i32
where
    Fut: Future<Output = i32>,
{
    f(1, 2).await
}

async fn example2<Fut>(f: impl FnOnce(i32, i32) -> Fut) -> String
where
    Fut: Future<Output = String>,
{
    f(1, 2).await
}

async fn example3<Fut>(f: impl FnOnce(String, String) -> Fut) -> String
where
    Fut: Future<Output = String>,
{
    f("xxx".to_owned(), "yyy".to_owned()).await
}

async fn example4<Fut>(aaa: &str, f: impl FnOnce(String, String) -> Fut) -> String
where
    Fut: Future<Output = String>,
{
    f("xxx".to_owned(), aaa.to_owned()).await
}

async fn example5<Fut>(f: impl FnOnce(&str) -> Fut) -> i32
where
    Fut: Future<Output = i32>,
{
    f("xxx").await
}

fn example6(f: impl FnOnce(&str) -> i32) -> i32
{
    f("xxx")
}

#[tokio::main]
async fn main() {
    let z = 3;
    let res = example(|x, y| async move { x + y + z }).await;
    println!("Hello, world! {}", res);

    let res = example2(|x, y| async move { format!("{},{},{}", x, y, z) }).await;
    println!("Hello, world! {}", res);

    let res = example3(|x, y| async move { format!("{},{},{}", x, y, z) }).await;
    println!("Hello, world! {}", res);

    let res = example4("xyz", |x, y| async move { format!("{},{},{}", x, y, z) }).await;
    println!("Hello, world! {}", res);

    //let res = example5(|x| async move { x.len() as i32 }).await;
    //println!("Hello, world! {}", res);

    let res = example5(|_x| async move { 999 }).await;
    println!("Hello, world! {}", res);

    let res = example6(|x| { x.len() as i32 });
    println!("Hello, world! {}", res);
}

解説

example1

Futureなクロージャーを引数で受ける方法はスタックオーバーフローのカービーが教えてくれた(自分が質問したわけじゃないけど)。example1はクロージャーの引数も戻り値もCopyできるもの。

example2

戻り値だけCopyできないもの

example3

引数もCopyできない。さらに借用ではない

example4

呼び出す関数に借用な引数を与えてみる

example5

クロージャーの引数を借用にしてみる。借用した値をクロージャーの中で使うとエラーになる。使わないなら動作する。

error: borrowed data cannot be stored outside of its closure
  --> src/main.rs:58:39
   |
58 |     let res = example5(|x| async move { x.len() as i32 }).await;
   |               ------------------------^^^^^^^^^^^^^^^^^^-------
   |               |        |              |
   |               |        |              cannot be stored outside of its closure
   |               |        ...because it cannot outlive this closure
   |               borrowed data cannot be stored into here...

error: aborting due to previous error

example6

Futureでないクロージャーなら借用を利用しても問題無い

関連リンク

How to accept an async function as an argument?