Go vs Rust? Choose Go.

Matthias Endler

GoとRust、どちらを選ぶ? Goを選ぼう。

Gopherize.meでデザインされたGopher。歯車はFreepikによるデザイン。
出典:Gopherize.meでデザインされたGopher。歯車はFreepikによるデザイン。

この記事を書いたのはずいぶん前です。その間に、いくつかの点について私の考えは変わりました。

長所と短所をよりバランスよく比較したい方には、Shuttle 🚀と共同で執筆した、GoとRustの比較記事を読むことをおすすめします。

RustとGo:実践的な比較

「RustとGo、どちらを選ぶべき?」これは、私がかなり頻繁に受ける質問です。どちらの言語も同じユーザー層をめぐって競合しているように見えますし、どちらもシステムプログラミング言語のように思えます。ということは、明確な勝者がいるはずですよね?

Go:実用的、実践的、そして平易

時間の経過に対するGolangの学習曲線。一直線に伸びている。
時間の経過に対するGolangの学習曲線。一直線に伸びています。

Goは洗練された言語だとは思いません。最大の特徴は単純さ、つまり簡単さです。そもそもGoはシステムプログラミング言語ですらありません。マイクロサービスやバックエンドインフラ向けのツールを書くには非常に優れていますが、Goでカーネルやメモリアロケーターを書きたいとは思いません。

しかしGoなら、仕事を片づけられます。しかも速く。
私がこれまで使ってきた中でも、Goは特に生産性の高い言語の一つです。モットーは「現実の問題を今日解決する」です。

Rustの強力な保証には代償がある

時間の経過に対するRustの学習曲線。波乱に満ちた道のり。
時間の経過に対するRustの学習曲線。波乱に満ちた道のりです。

それに比べると、Rustは難しい言語です。ある程度、生産的に使えるようになるまでに何か月もかかりました。メリットを実感するには、かなりの時間を投資する必要があります。Rustはすでに強力な言語ですが、日々さらに強くなっています。私にとっては、より安全なCというより、実用的なHaskellに近く感じられます。

誤解しないでください。私はRustが大好きですし、Rustのおかげでよりよいプログラマーになれました。学ぶ価値のある、素晴らしい言語なのは間違いありません。大きな問題は、次の大規模プロジェクトにとって本当に適切な選択なのか、ということです。

要するに、Rustを選ぶなら、通常はこの言語が提供する保証が必要なはずです。

  • Nullポインター、競合状態、その他あらゆる低レベルの脅威に対する安全性。
  • 予測可能な実行時の挙動(ゼロコスト抽象化とガベージコレクターなし)。
  • ハードウェアに対する(ほぼ)完全な制御(メモリレイアウトやプロセッサーの機能)。
  • 他の言語とのシームレスな相互運用。

これらの機能を必要としないなら、次のプロジェクトにRustを選ぶのは得策ではないかもしれません。こうした保証には、習熟までの時間という代償があるからです。悪い習慣を捨て、新しい概念を学ばなければなりません。使い始めたばかりの頃は、借用チェッカーと何度も格闘することになるでしょう。

ケーススタディ:試し割り法による素数判定

ある数が素数かどうかを調べたいとします。最も簡単な方法は、その数を、それより小さい自然数のどれかで割り切れるか(余りが出ないか)調べることです。どれでも割り切れなければ、素数が見つかったことになります。この方法を試し割り法と呼びます。

Golangでは次のように書けます(Rosetta Codeより)。

func IsPrime(n int) bool {
	if n < 0 {
		n = -n
	}
	switch {
	case n < 2:
		return false
	default:
		for i := 2; i < n; i++ {
			if n%i == 0 {
				return false
			}
		}
	}
	return true
}

Rustで同じことをすると、こうなります。

pub fn is_prime(n: u64) -> bool {
    match n {
        0...1 => false,
        _ => {
            for d in 2..n {
                if n % d == 0 {
                    return false;
                }
            }
            true
        }
    }
}

一見すると、どちらの解決策もかなり似ています。しかし、詳しく見るといくつか違いがあります。

  • Goでは単純なswitch-case文を使います。Rustでは、はるかに強力なmatch文を使います。
  • Goでは単純なforループで2からnまでの数を反復します。Rustでは、範囲式(2..n)を使います。
  • Goではreturn文を2つ使いますが、Rustではreturnを1つ使います。一般に、Rustではほとんどすべてが式であり、返り値にしたり変数に代入したりできます。式について詳しくはこちらを読んでください。

多くの分野で、RustはGolangよりも関数型の性質が強くなっています。上のコードは、Rangeに実装されているanyメソッドを使って書き直すこともできます。

fn is_prime(n: u64) -> bool {
    match n {
        0...1 => false,
        _ => !(2..n).any(|d| n % d == 0),
    }
}

最初は少し異質に見えるかもしれませんが、しばらくすれば自然に使えるようになります。

もちろん、これは簡単な一例にすぎません。2つの言語についてより感覚をつかむには、Rosetta Codeのコードをいくつか読んでみることをおすすめします。

ケーススタディ:テキストファイル内の重複語を見つける

目で見ながら学ぶタイプなら、GoとRustで単純な並行プログラムを書き、両言語を比較しているこちらの動画をご覧ください。

Goで気に入っている点

  • コンパイルが速い
  • 実践的な問題解決アプローチ
  • 一般的なDevOps作業に適した充実したエコシステム
  • 必要なものが一通り揃った標準ライブラリ
  • IDEのサポート
  • シンプルなエラーハンドリング
  • マスコット 😉

Rustで気に入っている点

  • 安全性:ヌルポインターがなく、データ競合もない……
  • きめ細かなシステム制御
  • 驚異的な実行速度(C/C++に匹敵)
  • ゼロコスト抽象化
  • 素晴らしく、オープンマインドなコミュニティ
  • cargoによるシンプルなパッケージ管理
  • トレイトによるジェネリクスのサポート
  • Cとの相互運用とFFI

結論

99%の時間は、Goで「十分」です。そして、Goでは十分でない1%に当たれば、そのことが分かるはずです。そのときはRustに目を向けてください。2つの言語は実によく補完し合うからです。実践的なRustコンサルティングに興味がある方は、私のカレンダーから日時を選んでください。どのようにお手伝いできるか、お話ししましょう。

結局のところ、RustとGoは、本当の意味では競合関係にありません

原文は Matthias Endler により に公開されました。

この記事は「gpt-5.6-luna」を使用して翻訳されました。