🦔

axumのHandlerで謎のコンパイルエラー

2024/07/23に公開

課題

axumのHandlerで、以下のコードが失敗する。

pub async fn get_entries(Extension(conn): Extension<Arc<Mutex<Connection>>>) -> impl IntoResponse {
    let conn = conn.lock().unwrap();
    let mut stmt = conn
        .prepare("SELECT id, name, created_at, updated_at from entries")
        .unwrap();

    let posts = stmt
        .query_map([], |row| {
            Ok(Post {
                id: row.get(0)?,
                name: row.get(1)?,
                created_at: row.get(2)?,
                updated_at: row.get(3)?,
            })
        })
        .unwrap()
        .map(|row| row.unwrap())
        .collect::<Vec<Post>>();

    // postsのnameを書き換える
    let updated_posts: Vec<Post> = update_url(arc_posts).await;

    (StatusCode::OK, Json(json!(updated_posts)))
}

こうすると、コンパイルエラーが出る。

error[E0277]: the trait bound `fn(Extension<Arc<std::sync::Mutex<Connection>>>) -> impl futures::Future<Output = impl IntoResponse> {posts::get_posts}: Handler<_, _>` is not satisfied
   --> src/main.rs:23:13
    |
23  |         get(controller::posts::get_posts).post(controller::posts::create_posts),
    |         --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extension<Arc<std::sync::Mutex<Connection>>>) -> impl futures::Future<Output = impl IntoResponse> {posts::get_posts}`

postsをそのまま返すとコンパイルが通るのに、updated_postsにすると上記のエラーになる。
情報量が少なくて何がなんだかわからなかった

解決までの道のり

debug_handlerの導入

axumのドキュメントを見ると、debug_handlerマクロを追加することで、エラーを詳細表示できるらしい。

+#[debug_handler]
pub async fn get_posts(Extension(conn): Extension<Arc<Mutex<Connection>>>) -> impl IntoResponse {

axum::handlerが満たすべき条件

ドキュメント
いわく、以下の条件(抜粋)を満たす必要があるらしい。

  • クロージャを使っている場合、Clone + Sendであり、'staticである
  • Sendなfutureを返却する

std::sync::Mutexをtokio::sync::Mutexに変更する

std::sync::Mutexはawaitをまたげない。

std::sync::Mutexを使っているところを別ブロックに切り出す

get_postsの中ではstd::sync::Mutexとtokio::sync::Mutexが共存している。
このとき、std::sync::Mutexのlock()をget_posts内の平場で呼びだすと、get_postsの最後までロックが開放されない(その結果、std::sync::Mutexがawaitをまたいでしまう)
std::sync::Mutexをブロック内に閉じ込めることで、awaitをまたがないようにする。

まとめ

公式ドキュメント大事(当たり前)。 特に、フレームワークとかだと自分が書いた場所じゃないところでエラーになったりするため、Rustのライブラリやフレームワークを使ってるときにデバッグ方法に迷ったらとりあえず公式ドキュメントをみるべき

Discussion