まず結論:この話は「速さ」より「安心感」の話
Corrode Rust Consulting の記事「Migrating from Go to Rust」は、Go から Rust へ移るときの論点をかなり正直に整理したガイドです。
面白いのは、「RustはGoより速いの?」みたいな単純な話ではないと最初に釘を刺しているところだと思います。
著者の主張をざっくり言うと、Go も Rust もサーバー開発には強い。
ただし Rust は、コンパイラがもっと多くのミスを事前に防いでくれる。その代わり、学習コストや書き方の制約は増える。
つまり、移行の価値は「性能アップ」よりも 正しさ・安全性・開発体験の改善 にある、という話です。
記事のキーポイント
- Go と Rust はどちらも静的型付き・コンパイル型の言語で、バックエンド開発に強い
- ただし Rust は、nil、エラー処理、データ競合、リソース管理を type system に寄せている
- Go は「やさしく始めやすい」、Rust は「間違いにくい」
- Go から Rust への移行理由は、速度不足よりも「細かい不満の積み重ね」であることが多い
- Rust の borrow checker は面倒でもあるが、慣れると「人間が頭でやっていた確認をコンパイラがやってくれる」
- 移行は一気にやるより、サービス単位で段階的に進めるのが現実的
- すべてを Rust に置き換えるべき、という話ではなく、Go を残す判断も十分ありうる
このガイドの前提:バックエンド向けの話です
この記事はかなり backend-focused です。
つまり、HTTP サーバー、gRPC、DB、API サービスみたいな領域が主戦場。
これはかなり大事な前提です。
Go はまさにこの領域で強いので、Rust への移行を考えるチームも、だいたいここにいます。
CLI、組み込み、ゲームエンジンでも参考にはなるけれど、記事として一番役に立つのはバックエンド開発者だ、という立て付けですね。
この割り切りは誠実だと思います。
「Rustは万能です」と言い出さないのがいい。移行の話って、つい理想論に寄りがちなので。
著者の立場はかなり率直
記事の書き手は最初からこう言っています。
自分は Go があまり好きではない と。
かなり強い言い方ですが、理由はちゃんと説明されています。
- Go は「簡単そう」には見えても、単純というより雑に見える部分がある
-
nilがあちこちに出る - エラー処理が型ではなく「作法」に寄っている
- 長らく generics がなかった
ただし、ここで大事なのは「嫌いだからダメ」とは言っていないことです。
実際、Go は広く使われているし、仕事で困らず動いている人もたくさんいる。
そこを認めたうえで、Rust への移行を考える人に向けて、比較の軸を整理しているわけです。
このバランス感覚は、私はかなり好感を持ちました。
技術記事って、推し言語の布教ポスターになりがちなので、こういう「自分の偏りも開示する」書き方は信頼しやすいです。
Go と Rust は、どこが似ていて、どこが違うのか
両方とも以下の共通点があります。
- 静的型付き
- コンパイル型
- 並行処理に強い
- バックエンドで使いやすい
でも、コンパイラがどこまで保証してくれるか が大きく違います。
Go はこう考える
- ガーベジコレクタ(GC)がメモリ管理を面倒見てくれる
- race detector などの runtime ツールで問題を見つける
- エラーは
if err != nilで都度確認する
Rust はこう考える
- ownership(所有権)でメモリの扱いを厳密にする
-
Send/Syncなどでスレッド安全性を型で見る -
Result<T, E>でエラーを型として表す
要するに、
Go は「動かしながら安全を確認する」寄り、
Rust は「コンパイル時に安全を詰める」寄り です。
個人的には、この違いはかなり本質的だと思います。
Rust の強みは「機能が多い」ことより、ミスを起こしにくい設計が最初から言語に埋め込まれていることなんですよね。
Rust の borrow checker は、何がうれしいのか
Rust でよく話題になるのが borrow checker(借用チェッカー)です。
これは、ざっくり言うと 「この値を誰が、いつ、どう使ってよいか」をコンパイラが厳しくチェックする仕組み です。
一見すると面倒です。
でも記事では、ここをかなりうまく説明しています。
たとえば Mutex<T>。
Go だと「このデータを触る前に lock を取るのを忘れないでね」という注意が必要です。
Rust だと、そもそも lock を取らないと中身に触れないようになっている。
lock() を呼ぶと guard が返ってきて、その guard を持っている間だけアクセスできる。
guard が drop されたら自動で unlock される。
つまり Rust は、
「うっかり忘れる」余地を型で潰しているんです。
これは地味ですが、めちゃくちゃ強い。
人間の注意力に頼る箇所が減るので、保守が楽になります。
もちろん最初は窮屈です。でも、慣れると「これ、むしろ脳のメモリを節約してくれるな」と感じる場面が増えるはずです。
Go から Rust へ移る理由は、だいたい「小さな不満の積み重ね」
記事が面白いのは、Go 開発者が Rust を検討する理由を「Go が遅いから」とは見ていないところです。
むしろ多くの場合、次のような細かいストレスが積み重なっているそうです。
- エラー処理が冗長
-
nilによる事故が怖い - generics が長くなかった
- enum や trait のような強い表現力が欲しい
- 標準ライブラリに「Set がない」など、地味な穴がある
この感覚はすごくわかります。
大事故より、毎日の小さいイライラのほうが移行理由として強いことってありますよね。
「なんとなく書きにくい」「なんとなく怖い」が積もると、別の言語に目が向く。かなり自然です。
1. nil の事故を Rust は型で減らす
Go でよくあるのが nil 問題です。
存在しない値をうっかり触ってしまい、実行時に panic するやつです。
記事では、たとえば「検索結果が見つからなかったときに nil が返る」ケースを挙げています。
Go では、呼び出し側が user != nil を忘れると、あとで user.Account.Notify() のような箇所で落ちることがある。
一方 Rust は Option<T> を使うので、
値があるかないかを明示的に扱わないといけない。
None の可能性を無視して値を取り出す、という雑なことはできません。
これはかなり大きいです。
「見つからないかもしれない」をコードの形で強制できるので、事故の種類が減る。
著者が「pager-duty incidents が減る」と冗談っぽく書いているのも納得です。夜中に起こされる事故は、できるだけ減らしたいですからね。
2. data race は、Go では実行時に見つかることがあるが、Rust ではコンパイル時に止まる
Go では go test -race が強力な武器です。
でもこれは runtime detector なので、実際にその競合がテストで起きないと見つからないという弱点があります。
たとえば複数 goroutine から map をロックなしで更新するようなコードは、Go ではコンパイルできます。
そして本番負荷で突然壊れることがある。
Rust では、共有可変状態をスレッド間で扱うには Send / Sync などの条件を満たす必要があります。
単純な HashMap をそのまま共有しようとすると、そもそもコンパイルが通らないことがある。
これは、かなり痛快です。
「壊れるコードを実行前に止める」わけですから。
もちろん Rust でも何でも安全、というわけではありませんが、少なくとも data race のクラスはかなり前倒しで潰せます。
3. エラー処理は、Go の作法より Rust の型のほうが“積み上がり”に強い
Go の if err != nil { return err } は、最初はシンプルです。
でも長く使うと、次のような不満が出ると記事は指摘しています。
- コードがエラー処理で埋まる
- 文脈を足す
fmt.Errorf("...: %w", err)を忘れやすい - 新しいエラーの種類を見落としてもコンパイラが教えてくれない
Go 側にも反論はあって、lint や errcheck がかなり助けになる、というのはその通りです。
記事もそこは認めています。
それでも Rust の Result<T, E> は、
「失敗する可能性」を関数の型として明示する のが強い。
? 演算子でエラーを上に流せるので、書き味も意外と悪くない。
著者も「? は explicit だ」と見ています。
このへんは好みが分かれますが、私は Rust のほうが「読み返したときに安心」だと思います。
Go の明示的な if err != nil は確かに読みやすい。
でも Rust の ? も、慣れると「ここは失敗を上に任せている」と一目でわかるので、十分に明快です。
Go を残すべきか、Rust に移るべきか
この記事は、Rust への全面移行を煽っていません。
むしろ、Go が向いている場面もある という前提がかなり強いです。
Go を続ける価値があるのは、たとえばこういう場合だと思います。
- 開発スピードを最優先したい
- チームが Go に慣れている
- シンプルなサービスを素早く回したい
- 多少の runtime リスクは lint や運用で吸収できる
一方、Rust を検討したくなるのは、たとえばこういう状況でしょう。
- バグのコストが高い
- 並行処理の安全性をもっと強く保証したい
- メモリやリソース管理を厳密にしたい
- 長期運用で「小さい事故」を減らしたい
個人的には、Rust の価値は「最高速度」よりも、大規模運用での安心感 にあると思います。
Go はとても良い言語ですが、「気をつけていれば大丈夫」を積み上げる文化に寄りやすい。
Rust は「気をつけなくても壊しにくい」方向に寄っている。
この違いは、長く使うほど効いてくるはずです。
移行は一気にやらず、段階的に進めるのが現実的
記事の最後では、Go サービスを Rust に移すときは段階的な移行が重要だと示唆しています。
これはかなり実務的です。
いきなり全部を書き直すのは、だいたいしんどいです。
現実には、周辺の小さいサービスや、新規機能、一部の性能クリティカルな部分から Rust にしていくほうが安全でしょう。
この「全部リライトしない」という姿勢は大事です。
技術移行で失敗する典型は、たいてい「理想の完成形」を先に決めすぎることだと思います。
Rust は強いけれど、移行コストは確実にある。だからこそ、少しずつが正解になりやすい。
まとめ:Go と Rust は競争相手というより、得意分野の違う道具
この記事を読んで感じるのは、Go と Rust を「どっちが勝ちか」で見るより、
何をどこまでコンパイラに任せたいか で選ぶのが本質だ、ということです。
Go は軽快で、学びやすくて、バックエンドにとても強い。
Rust は厳しいけれど、その厳しさが安全性と保守性に直結する。
なので、Go から Rust への移行は「性能が足りないから」ではなく、
事故を減らしたい、設計の曖昧さを減らしたい、長期運用を楽にしたい という欲求から始まることが多い。
この見立てはかなり現実的で、私はとても納得感がありました。
Rust はたしかに学習コストが高いです。
でも、そのコストを払う価値がある現場も確かにある。
記事はその境界線を、かなり誠実に描いていると思います。