Web系エンジニアのアウトプット練習場

エンジニアリングと書評が中心。たまに全然関係無い話もします。
トップページ/プログラミング/Rustのファーストインプレッション「Rustプログラミング入門」/
2020年09月14日

Rustのファーストインプレッション「Rustプログラミング入門」

プログラミングRust

「Rustプログラミング入門」を読んで、Rustに入門してみたのでRustに対するファーストインプレッションを書き綴っておこうかなと思います。
「Rustプログラミング入門」の所感というよりもRust言語に対する所感が中心となりますのでご注意ください。

入門のきっかけ


今回入門したRustに関しては、実務で使う予定は今のところ無いのですが、近年WebAssemblyの文脈でよく聞くようになり、興味を持っていました。
また、今は時間の都合上参加できていませんが、競技プログラミング界隈でも高速なプログラムが書けるという評判をよく耳にするようになりました。

また、Webのバックエンドでも使われ始めていて、Rustやその周辺パッケージの更新もある程度安定してきたということを聞きつけたのと同時に、ちょうど良さげな入門書も発売されたということで思い切って手にとってみました。

入門の難易度


Rust言語の入門難易度ですが、今まで触ったことのある言語がどのようのものだったかによって、人それぞれ感じ方が異なってくると思います。
私が今まで実践で触ってきた言語としては、以下のようなものがあります。

  • C/C++
  • C#
  • Ruby
  • Python
  • TypeScript(React, Vue, Node等)
  • Go


新しい言語は他言語から良いとこ取りをしたようなものが多く、Rustも例外ではありません。

例えば、Rustにはasync/awaitで非同期処理を行うことができますが、これはJavaScriptにも同じ構文があるため、JavaScriptの使用経験があるとすんなり理解できると思います。
実際「Rustプログラミング入門」でもJavaScriptのそれとほぼ同等の機能であるというような説明がされています。
「Rustプログラミング入門」では他にも「トレイト(trait)はJavaのインターフェース(interface)のような機能」といった説明もされており、前書きにも書いてあるように他言語を使用したことがある読者を対象に書かれた入門書であることが伺えます。

さて、入門の難易度ですが、ファーストインプレッションとしては「難しい」と感じました。
入門が難しいと感じた点は主に3点あります。

  • 「メモリ安全」や「スレッド安全」であること担保するための機能
    • 所有権と借用
    • ムーブセマンティクスや変数のライフタイム決定の仕組みについての理解が必須
  • マクロの定義の仕方
  • 文(statement)と式(expression)の使い分け


「メモリ安全」や「スレッド安全」であること担保するための機能


そもそも他の多くの言語では「メモリ安全」や「スレッド安全」を自分で担保する必要があります。
(メモリ安全に関しては、速度を犠牲にガベージコレクションを採用している言語もあります)

他の言語の方がプログラムの書き方自体は簡単でも、「速度」と「メモリ安全、スレッド安全の担保」を両立することはとても難しいのです。
そういう意味で、Rustの方が入門は難しいですが、慣れてしまえば他言語よりもRustの方が高速で安全なプログラムが簡単に書けそうな気がしています。

マクロの定義の仕方


マクロの書き方で面食らったので、強く印象に残ったポイントです。
例えばこんなん。

macro_rules! console_log {
    ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}


これについても慣れてしまえば、Cの#defineマクロよりも安全にマクロを定義できそうですが、入門時にはつまづきやすいポイントかなと思いました。
よって、Rustのマクロの仕様については、しっかり理解に時間をかけるようにしました。

文(statement)と式(expression)の使い分け


これについては、「Rustプログラミング入門」の中では詳しい言及がないため、つまづきました。
サンプルコードを写経すると気づくのですが、行によってセミコロンがついていたり、ついていなかったりします。

そのコードが文(statement)の場合はセミコロンが必要で、式(expression)の場合はセミコロンをつけません。
式の場合でも、式の結果を使用しない場合はセミコロンをつけてもコンパイルエラーにはなりません。

特に特徴的だなと思ったのがRustのifです。
Rustではifが式なので、以下のように書けます。

// numberの絶対値をresultに格納
let number = 1;
let result = if number >= 0 {
    number
} else {
    -number
};


他言語で言うところの三項演算子的なものが普通にifで書けるというわけです。

これはシンプルに書けて可読性は高いのですが、セミコロンを付けたり付けなかったり混乱することがあり、フォーマッタでも不要なセミコロンを検出できません。
どれが式で、どれが文か、考えながら書くのが難しいと感じました。

Rustに対する印象


なんといっても、「言語仕様レベルでメモリ安全とスレッド安全を担保できて、かつ高速な実行ができるのは凄い」が最初の感想です。

私はC/C++での開発経験がありますが、メモリの解放忘れによる不具合にはよく悩まされました。
静的解析ツールで見つかることもあるのですが、やはり限界もありますので...

最近はガベージコレクションを採用している言語が多く、私の関わっているプロジェクトはそれで事足りることも多いのですが、高速な実行が求められるような場合にはRustは必ず候補に上がることになるでしょう。

冒頭でも書きましたが、すでにWeb界隈でもバックエンドに採用されはじめ、フロントエンドでもWebAssemblyが登場し、Rustの顔が見え隠れし始めています。
Rustは数年後にはもっとメジャーな言語になっているだろう、と予感させられるポテンシャルを感じました。

ただ、普及するにあたって、先述した通り入門の難しさがあるので、Rustを使える人材がチームとして最初から揃うことはなかなか稀なのではないかなと思います。
チーム全員でRustを勉強しながらどんどん使える人材を増やしていく必要がありそうです。
その点、cargo docでドキュメントを自動生成できるなど、Rustコミュニティはドキュメントに力が入っているようなので、取るに足らない心配なような気もしています。

競プロでの使用


私は元々Pythonで参加している競プロの話です。
今回はRustを使ってAtCoderの問題を数問解いてみたのですが、多くの方が紹介している通り、「実行時間は速いけど、実装には時間がかかる」なと感じました。

型を強めに意識しなければなりませんし、Pythonに比べて単純にタイプ量が多くなります。
ただし、C/C++並に実行時間が速いので、想定解なのにTLEしてしまうといった心配はまずありません。
そのため、コンパイルさえ通れば、自信を持って提出できる印象です。

また、cargo-competeが非常に優秀で、コマンド1発でサンプルケースをチェックと提出を実行してくれたりします。
このツールの使い勝手も含めてPythonからRustに乗り換えるのはアリな気がしています。

まとめ


とにかく、高速かつ安全な言語なので、速度を最重視しなければならないようなプロジェクトではRust一択かなと感じました。
しかし、実行速度をそこまで優先しなければならないプロジェクトが今のところないため、とりあえず競プロの問題で肩慣らししておこうと思います。
将来的にWebAssemblyなど、Webフロントにまで進出してくる気配があるので、今のうちにある程度使えるようになっておいた方が良いかもしれませんね。