axum の次のリリースの移行が大変そう
こんにちは,なっふぃです.
久しぶりに axum の CHANGELOG.md を読んでみたら,今年の 4 月以降に #Unreleased セクションに追加された項目がかなり重そうでした.次のリリースのタイミングでは多くの修正作業が必要になると思われます.
この記事は,2023-04-30 のコミット #b663072504c7846556adcc0dea304c59f70c986a での最新の CHANGELOG.md に基づいています.
アイキャッチは axum というラテン語(英語:axle)から取りました.
大きな修正
私の理解した限りでは,Unreleased は
- Body を表す型パラメータの削除
- hyper v1.0 への追従
がメインとなっています.
前者はより user-friendly な設計を目指した修正ですが,現行バージョン (v0.6.x) からの移行が大変そうです.
後者は,現在開発中の hyper 1.0 が多くの breaking changes(後方互換性の無い変更)を含んでいるので,それらへの対応になります.
hyper 1.0 について詳しくは hyper 1.0 Roadmap を確認してください.
以下,主要な変更点を見ていきます.
Body を表す型パラメータの削除
#1751/#1778/#1789
(also related to hyper 1.0)
PR #1751 によると,主に次の理由で,型パラメータ B
を削除しています:
-
B
を変えるようなミドルウェア(例:DefaultBodyLimit)を使用するときに,使い方が難しい. - 新規ユーザの学習コストが高い.
元々,axum では多くの型やトレイトが B
という型パラメータを持っていました.
たとえば Router<S, B>
,FromRequest<S, B>
,Next<B>
などです.
この B
は「リクエストの body の型」を表していて,
Request<B1> -> Request<B2> -> Request<B3> -> ...
のように変換されていくことが想定されていました.
実際に axum を使っている 1 ユーザとしては,これは非常に鬱陶しく,「B1 != B2
だからコンパイルが通らない」ということが何度もありました.
その度に
fn my_router() -> Router<Body> {
}
とすべきか(B
に具体的な型 hyper::Body
を入れる),
fn my_router<B: HttpBody + Send + 'static>() -> Router<B> {
}
とすべきか(B
に関して generic),頭を悩ませています.
後者は B: HttpBody
のような trait bound が必要になることもあり,複雑で冗長になりがちです.
それならいっそ B
という型パラメータは削除して,B = axum::body::Body
のみを許すことにしよう,という話です.
なお,これによってパフォーマンスが低下する恐れはあります.型 B1, B2, ...
を最適化することができず,axum::body::Body
へ変換する必要があるからです.
ただし,この影響は無視できると述べられています (#1751):
The performance impact should be negligible since
Body
prevents double boxing (https://github.com/tokio-rs/axum/blob/v0.7.0/axum-core/src/body.rs#L103)
文中のリンクはすでにマージ & 削除済のブランチ(PR #1583)を指しているので,リンク切れになっています.
おそらくこちら のことだと思います.
関連する変更
型パラメータ B
の削除に伴い,PR #1789 でいくつかの変更が行われました.
こちらは機械的な書き換えで対応できるので,そこまで影響は無いと思われます.
Extractor やレスポンスで直接 body を扱っている場合には,ドキュメントをしっかり確認した上で直す必要がありそうです.
それ以外の場合は,コンパイラのエラーメッセージに従って修正するだけで足りるのではないでしょうか.
hyper 1.0 への対応
現在 hyper という crate は,バージョン 1.0 へ向けて開発が進められています.
hyper 1.0 の詳しい変更点は hyper 1.0 Roadmap で説明されていますが,今後の stability を見据えていくつかの機能が削除されます.
そのうち axum に影響を与えそうなものは,以下の変更です (#1325):
-
tower::Service
の削除, -
hyper::Server
の削除, -
hyper::Body
の削除.
依存する crates のアップデート待ちなど,ブロッキングな作業もあるみたいです.(#1882)
tower::Service
の削除
tower-service
への依存をやめ,独自の hyper::Service
へ移行されます.
新しい hyper::Service
の機能は tower::Service
と同じですが,poll_ready()
メソッドを消して簡略化しています.
これにより axum
の内部実装は影響を受ける一方,ユーザは引き続き tower
のエコシステムを利用できるはずだと思っているのですが,どうなんでしょう.
hyper::Server
の削除
hyper::Server
は,将来の前方互換性のため hyper-util
crate へ移動されます.
axum
では,代わりに axum::serve()
関数を提供するようです.(#1868)
- axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
- .serve(app.into_make_service())
- .await
- .unwrap();
+ let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
+ axum::serve(listener, app).await.unwrap();
tokio::net::TcpListener
を使うには tokio/net
feature が必要です.
hyper::Body
の削除
私の理解する限りでは,Body
を削除しなければいけない積極的な理由は分かりませんでした.
HTTP 3 以降で body の新しい実装が必要になったときのためでしょうか.
hyper::Body
は hyper::body::Empty
などいくつかの型に分割されます.
axum
では,「Body を表す型パラメータの削除」で述べたようなように,axum::body::Body
へ移行します.そのため直接的な影響はありませんが,hyper::Body
へ依存しているユーザは注意が必要です.
所感
機械的な変更で済む部分もありますが,body に関する修正はしっかり確認しないとコケそうです.
ただ,B
パラメータに悩まされてきたのは事実なので,ここで削除する方向に舵を切るのは良いのかもしれません.
axum::serve()
は今までより簡単にサーバを起動できるようになるので楽しみです.
全体的に「移行するのは苦痛だけど,一度対応すれば長い目で見て楽になる」タイプの変更が多い印象なので,頑張って対応しようと思います.
Discussion