🦔
axumのHandlerで謎のコンパイルエラー
課題
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