64132


flat_map内でFuture functionをコールした場合  
どのようにasyncをflat_mapに記述すれば良いですか。

[code]

    async fn gen_str() -> Result<Vec<String>, String> {
        Ok(vec!["a".to_string(), "b".to_string()])
    }
    async fn gen_list(msg: &str) -> Result<Vec<String>, String> {
        Ok(vec![format!("1.{}", msg), format!("2.{}", msg)])
    }
    
    #[async_std::main]
    async fn main() {
        let v = gen_str().await.unwrap();
        let list: Vec<String> = v
            .iter()
            .flat_map(|n| gen_list(&n).await.unwrap())
            .map(|n| n.into())
            .collect();
        println!("{:?}", list);
    }
    
    
[/code]

上記のコードだと下記のエラーが出てしまいます。

[code]

    `await` is only allowed inside `async` functions and blocks
    
    only allowed inside `async` functions and blocksrustc(E0728)
    
[/code]


CC BY-SA 4.0
2020-03-26T03:27:29.693
64123
2020-03-26T10:53:57.783
32285
question
rust
flat_mapの中で async/awaitを使う場合


こんにちは。  
質問への直接的な回答、つまり `flat_map` の中で `async` の文脈を作るには `|n| async {
gen_list(&n).await.unwrap() }` のように、 `async {}`
ブロックで囲んであげることですが、これだとコンパイルは通りません。

やりたいことは `Vec<impl Future<Item = Vec<T>>>` なる型を `impl Future<Item=Vec<T>>`
へ変換したいということだと思います。こういう入れ子になった型の中にある `Future`
を外側にもってくるのは少し大変です。変換に使うメソッドや関数は標準ライブラリにはないので
[futures](https://crates.io/crates/futures) の
[`join_all`](https://docs.rs/futures/0.3.4/futures/future/fn.join_all.html)などを用いて変換します。  
`Vec<impl Future<Item = Vec<T>>>` → `impl Future<Item = <Vec<Vec<T>>` → `impl
Future<Item=Vec<T>>` の手順で変換すればよさそうです。

[code]

    use futures::future::join_all;
    
    async fn gen_str() -> Result<Vec<String>, String> {
        Ok(vec!["a".to_string(), "b".to_string()])
    }
    async fn gen_list(msg: &str) -> Result<Vec<String>, String> {
        Ok(vec![format!("1.{}", msg), format!("2.{}", msg)])
    }
    
    async fn main_() {
        let v = gen_str().await.unwrap();
        // `Vec<impl Future<Item = Vec<T>>>` → `impl Future<Item = <Vec<Vec<T>>`
        let v = join_all(v.iter().map(|n| gen_list(&n))).await;
        // `impl Future<Item = <Vec<Vec<T>>` → `impl Future<Item=Vec<T>>`
        let list: Vec<String> = v
            .into_iter()
            .flat_map(|l| l.unwrap())
            .map(|n| n.into())
            .collect();
        println!("{:?}", list);
    }
    
    
[/code]

この他にもう1つの方法があって、コンビネータを使うのをやめて手続き的な処理を書くと、エラーが起きなくなります。元のコードでコンパイルエラーになっていたのはクロージャの内側では
`async` の文脈にならないのが原因だったので、クロージャを使わない書き方をすると問題にならなくなります。

[code]

    async fn gen_str() -> Result<Vec<String>, String> {
        Ok(vec!["a".to_string(), "b".to_string()])
    }
    async fn gen_list(msg: &str) -> Result<Vec<String>, String> {
        Ok(vec![format!("1.{}", msg), format!("2.{}", msg)])
    }
    
    async fn main_() {
        let v = gen_str().await.unwrap();
        let mut list = Vec::new();
        for n in &v { 
            let n = gen_list(&n).await.unwrap();
            list.push(n);
        }
    
        println!("{:?}", list);
    }
    
    
[/code]

ただしこちらは `Future`
1つ1つ順番にスケジューリングされるので非同期処理の観点からはあまり好ましくないはずです。型合わせだけが必要な場合にのみ試してみて下さい。


CC BY-SA 4.0
2020-03-26T10:53:57.783
64132
2020-03-26T10:53:57.783
22816
64123
answer