本記事は、2022年11月に開催された
私はマップボックスジャパン合同会社という会社でソフトウェアエンジニアをしているyukiです。Twitterでは@helloyuki_で、TechFeedではRustの公認エキスパートとして活動しています。最近は本や連載記事を書くことも多かったのですが、そのほかにはRust Tokyoの運営をしたり、

MapboxでのRust
本日のイベントはMapboxがスポンサーをしていますので、どのようなことをしている会社かを簡単に説明すると、主に地図技術やナビゲーションのAPIを作っているアメリカの会社です。実は社内ではRustをメインで使用しているわけではなく、どちらかというとTypeScript、JavaScript、C++のコードが多く、一部コンポーネントでRustを使用しています。最近ベラルーシオフィスのC++エンジニアがRustを書いているのを見て少しテンションが上がりました。
弊社では高トラフィックなサーバや検索などにRustを利用しており、負荷が高い/速度が求められるところにRustを利用する傾向が高まりつつあります。また、Linux向けの組み込みソフトウェアをRustで書いてみてはどうかという提案が社内であり、このような使い方も広がるのではないかと思っています。

発表の前提と目的
本日は中上級者向けの発表として、Rustの基礎文法に習熟していることを前提としており、既存の文法や機能の解説はほぼしません。また、私自身の理解でお話しますので、もし間違っているところがあればご指摘ください。

今回の発表では、私個人が

2022年の変更をおさらいする - 標準ライブラリ/文法機能面/周辺ツール
2022年は2021年に比べると多くの機能が入ったという印象があります。

そこで、2022年の変更を次の3つのグループに分けてご紹介し、おさらいしたいと思います。
- 標準ライブラリへの変更
- 文法機能面への変更
- 周辺ツール
(cargoなど) への変更

標準ライブラリへの変更
標準ライブラリへの変更の中で個人的に大きいと思ったのは、次の2つです。
- [1.
62, 1. 63] Mutexの内部実装への変更とconst文脈化 - [1.
63] Scoped Threadの導入

[1.62, 1.63]Mutexの内部実装への変更とconst文脈化
Mutex
さらにこの修正の副産物として、ヒープ領域を使う必要がなくなるため、Mutexがconst文脈で使えるようになりました。従来はMutexをOnceCellやlazy_

従来、MutexはBox<pthread_
をラップしたものでしたが、pthread_

この修正はRustコアチームのMara Bos氏が中心になって実施されましたが、そのときの話をRustConf 2021の動画で知ることができるのでおすすめです。課題を細かく分割し、解決できそうなものから解決するというエンジニアリングのアプローチをとった話があり、プルリクエストの差分と合わせて見ていくと大変おもしろいかと思います。ちなみにこの修正にはけっこうな年月がかかっており、大変な苦労もうかがえます。

この修正で内部実装にBoxが必要なくなったため、従来はOnceCellなどのクレートと組み合わせてstaticでMutexを利用していましたが、その必要はなくなり、Mutexを使うコードがきれいに書けるようになりました。これは個人的にも大きな修正だと感じていて、 経緯も含めて自分の開発でも参考にしたいものでした。

[1.63]Scoped Threadの導入
Scoped Threadはバージョン1.

こちらは従来の実装例です。std::

scoped_

余談ですが、実はRust1.

このLeakpocalypse

文法機能面への変更
文法機能面への変更からは次の4つをご紹介します。
- [1.
65] Generic Associated Typesが利用可能に - [1.
65] let-elseが利用可能に - [1.
62] enumのデフォルトのヴァリアントを指定可能に - [1.
64] awaitはIntoFuture:: into_ futureに脱糖されるように
これらのうち1と2は最近入ったもので、これから使っていきたいと思える変更です。

[1.65]Generic Associated Typesが利用可能に
Generic Associated Types

次の例は、イテレータをGATsを使って実装し直すもので、従来はtraitの関連型にライフタイム注釈や型引数をつけることはできなかったのですが、それがGATsで可能になったことで、関連型単位でのライフタイムを持たせるように実装できるようになりました。

私は実務でGATsを使ったことはまだないのですが、どのような使い方ができるかは考えているところです。
このような機能は

[1.65]let-elseが利用可能に
let-elseも1.@if let Some(b) = a {} else {}
)let Some(b) = a else {}
と書けるようになりました。この変更により、ネストが減ってコードの見た目がスッキリしたり、処理の流れが読みやすくなったりというメリットを享受できるようになります。

こちらのコードはif let Someをlet-elseで書き換えた例です。コメントアウトされた元のコードにはネストがありますが、let-elseを使うことでスッキリと書けていて、便利なのでこれから使っていけると思います。

ただし注意点として、elseの中には発散する型

ここでnever型について解説しておきます。Rustでは!として表現される型をnever型と呼び、loop / continue / returnは実はこの型を返しています。never型は値を何も返さないことを表現します。雑な言い方をすると、never型を返すとどの型にでもなることができるということで、たとえば返り値の型としてi32型を要求する関数内でnever型を返しても問題はありません(never型がi32型と同等とみなされます)。todo!マクロもnever型を返し、まだ実装していない・
このnever型はコンパイル時のデータフロー解析に使われ、never型を返す関数のあとにある処理には決して到達しないことを示しておき、以降の命令を成果物に含める必要がない、などの判断のために存在しています。

[1.62]enumのデフォルトのヴァリアントを指定可能に
この変更はバージョン1.#[default]
というアトリビュートをヴァリアントに指定して、Default::
に対応するヴァリアントを指定できるようになりました。ただし、ユニット型のヴァリアントのみに付与できるという制約があります。
この例のように、State::
を呼び出すと#[deafult]
がついたヴァリアントが取得できます。

[1.64]awaitはIntoFuture::into_futureに脱糖されるように
この変更はバージョン1.

なお、脱糖とは、左のコードのように書くと、Futureで処理をするように内部で右のようなコードに直してくれるようなイメージです。

周辺ツールへの変更
私が注目しているのは次の3つです。
- [1.
60] cargo build –timingsが安定化 - [1.
62] cargo addでクレートの依存を追加できるように - [1.
64] ワークスペースの共通設定が書けるように

[1.60]cargo build –timingsが安定化
cargo build –timings
はどのクレートのビルドにどれくらい時間がかかったか計測したり、ビルド時のリソースの使用状況などを計測したりできる機能です。nightlyにはすでにありましたが、バージョン1.

このコマンドを使うと、各クレートのビルドにかかった時間を把握できるので、ビルドのパフォーマンスチューニングなどに利用できます。

[1.62]cargo addでクレートの依存を追加できるように
これは嬉しかった機能で、コマンドひとつでクレートの依存をCargo.

[1.64]ワークスペースの共通設定が書けるように
cargoにはワークスペース機能がありますが、ワークスペースのルート側のCargo.

こちらはルート側のCargo.anyhow.
として共通設定を使用することを指定しています。もちろん、プロジェクト個別で使用するクレートも記述できます。

実務での新機能への対応
最後に、私が実務で新機能にどのように対応しているかを共有したいと思います。
正直なところあまりまじめにやっているわけではなく、取り入れたいものがあったら取り入れるようにしていて、無理に新機能を取り入れることはありません。たとえば、先日let-elseを使いたくなったので、チームでRustのバージョンを1.
新機能への対応/キャッチアップのスタンスは下記の通りで、基本的にはリリースノートに軽く目を通しておいて、使いたいときに使う程度で、積極的にキャッチアップしているわけではないです。
- 標準ライブラリへの変更 - docくらいは読んでおいて、使えるときに思い出して使う
- 文法や機能への変更 - リリースノートは読んでおいて、使えるときに思い出して使う
- 周辺ツールへの変更 - 何が入ったかは覚えておいて、使えるときに思い出して使う
なお、Mutexの変更のようにパフォーマンス面で恩恵を受けられる修正が入ることもあるので、バージョン自体はこまめに上げるようにしておいたほうがいいかもしれません。ただしバージョンを上げると一部のクレートがビルドできないなどの問題がでることもあるので、受けられる恩恵とのトレードオフがあります。

まとめ
今回の発表では、2022年にRustに入った機能/変更を駆け足で振り返りました。時間の都合上、細かい議論は省略しましたが、詳しくはZennのスクラップにまとめていますので、よろしければご参照いただき、本日のスライドに含まれる各資料へのリンクもたどってみてください。
以上で発表を終了します。ありがとうございました。