あれやこれやと

自転車、プログラミング、登山など思いついたままに書いていこうと思っています

Rust async は意外とツライ件

忘れないようにサクサクとメモ書きです。

普段趣味で書いていたのはC#C#erです。

 

そんな私がRust 1.39でasync/awaitが入ったということで喜んでがりがり使ってみようと

思ったけどあんまりうまく行かなかったって話です。

Rust初心者がasync/await使ってハマったのは・・・・

  • traitの実装ではではasync関数使えない。ただしasync-traitのクレートを使うと行けるようです。
  • trait内のasync関数ではSendではなくSyncの実装が必要っぽい?試行錯誤していたらSyncが足りないみたいなエラーが出たりしてまいました。
  • async fn で定義した関数で再起呼び出しすると、BoxFutureを戻り値にとか言われて混乱したので最終的に通常の関数内にasync blockを作ってblock_onメソッドで実行するようにした(本末転倒気味)
  • 上記のような関数に対して、Fn(a: &str) -> bool みたいな関数をフィルタとして引数として取ろうとしたら、スレッドセーフじゃないからダメだぜ!って言われて途方に暮れる。

きっとネイティブで動く故の制限だったりするのだろうか。特にスレッドセーフにならないような状況が推測できるときにコンパイルエラーになるようです。

 きっと回避策はあるのでしょうが、どうすりゃいいの!?!?ってなっていまして(;´∀`)

impl Hoge {    
    fn search_dirs(&mut self, dir: PathBuf, func: Box bool>) -> Result<Vec> {
        let mut result:Vec = Vec::new();
        let result_task:BoxFuture<Result<Vec>> = async move {
            let mut dir = fs::read_dir(dir).await?;
        
            while let Some(entry) = dir.next().await {
                let entry = entry?;
                let file_type = entry.file_type().await?;
                if file_type.is_dir() {
                    // ディレクトリの場合は再起で深堀していきます
                    let r = self.search_dirs(entry.path(), func)?;
                    result = result.into_iter().chain(r.into_iter()).collect();
                } else {
                    if func(&entry.path()) {
                        result.push(entry.path());
                    }

                }

            }
 
            Ok(result)
        }.boxed();
        
        async_std::task::block_on(result_task)
    }
}

例えばこんな感じに作ってたら、

    `dyn for<'r> std::ops::Fn(&'r async_std::path::pathbuf::PathBuf) -> bool` cannot be sent between threads safely

とか言われちゃいましてね。
まだまだ勉強不足を痛感するところです(´・ω・`)

C#だと結構この辺意識せずすいすい使えたので、シンドイといか結構新鮮というかw

 

メモ書きなんでこの辺で。。。