Go vs Rust? Choose Go.

Matthias Endler

Go vs Rust?選ぶならGo

原文は Matthias Endler により に公開されました。 このブログを購読する

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

この記事はかなり前に書いたものだ。その後、一部の点については考えが変わっている。

よりバランスの取れた長所・短所の視点を提供するため、Shuttle 🚀 と共同で執筆したこちらの Go vs Rust 比較記事を読むことをおすすめする。

Rust vs Go: A Hands-On Comparison

「RustとGo、どちらを選ぶべきか?」という質問をよく受ける。両言語は同じユーザー層を取り合っているように見えるし、どちらもシステムプログラミング言語のように見える。なら、はっきりした勝者がいるはずだと思うだろう?

Go:実用的、プラグマティック、シンプル

時間の経過とともに見るGolangの学習曲線、一直線。
時間の経過とともに見るGolangの学習曲線、一直線。

Goがエレガントな言語だとは思わない。Goの最大の特徴はシンプルさ 簡単さだ。Goはそもそもシステムプログラミング言語ですらない。バックエンドインフラ周りのマイクロサービスやツールを書くのには素晴らしいが、カーネルやメモリアロケータをGoで書きたいとは思わない。

だがGoなら、物事を素早く片付けられる — とにかく速い。
Goは私がこれまで使ってきた中で最も生産的な言語の一つだ。合言葉は「現実の問題を、今日解決する」だ。

Rustの強力な保証には代償が伴う

時間の経過とともに見るRustの学習曲線、波乱の道のり。
時間の経過とともに見るRustの学習曲線、波乱の道のり。

それに比べてRustは難しい。ある程度生産的になるまでに何ヶ月もかかった。何らかの恩恵を感じるには、相当な時間を投資する必要がある。Rustはすでに強力な言語だが、日々さらに強力になっている。私にはRustは「より安全なC」というより「実用的なHaskell」のように感じられる。

誤解しないでほしい。私はRustが大好きだし、Rustのおかげでより良いプログラマーになれた。学ぶには間違いなく素晴らしい言語だ。大きな問題は、それが次の大きなプロジェクトに適した選択かどうかだ。

要点はこうだ。Rustを選ぶなら、通常はその言語が提供する保証が必要だからだ。

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

これらの機能のいずれも必要としないなら、Rustは次のプロジェクトには不向きかもしれない。なぜなら、これらの保証には代償、すなわち習得までの時間がかかるからだ。悪い習慣を捨て、新しい概念を学ぶ必要がある。始めたばかりの頃は、かなりの確率でborrow checkerとの戦いを強いられるだろう。

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

たとえば、ある数が素数かどうかを判定したいとしよう。最も簡単な方法は、その数をより小さい自然数で割り切れるかどうかを調べることだ(余りなしで)。割り切れなければ、それは素数だ。この手法は試し割りと呼ばれる。

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では 2 から n までをループするのにシンプルなforループを使う。Rustでは範囲式(2..n)を使う。
  • Goでは2つのreturn文を使っているが、Rustでは1つのreturnを使っている。一般的に、Rustではほとんどのものが式であり、returnしたり変数に代入したりできる。式について詳しくはこちらを参照。

多くの点で、RustはGolangよりも関数型寄りだ。上記のコードは、Rangeに実装されているanyメソッドを使って書き換えることもできる。

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

最初は少しとっつきにくく感じるかもしれないが、しばらくすれば自然に身につく。

もちろん、これはほんの簡単な例だ。両言語の感覚をより掴むために、Rosetta Codeでコードを眺めてみることをおすすめする。

ケーススタディ:テキストファイル内の重複単語を探す

視覚的に学びたい人向けに、GoとRustでシンプルな並行プログラムを書いて両言語を比較する動画を用意した:

Goで気に入っている点

  • コンパイルが速い
  • 実用的な問題解決アプローチ
  • 定番のDevOpsタスクに適した充実したエコシステム
  • バッテリー同梱の標準ライブラリ
  • IDEサポート
  • シンプルなエラーハンドリング
  • マスコット 😉

Rustで気に入っている点

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

結論

99%の場合、Goで「十分」だ。残りの1%でGoでは足りないときは、きっと分かるはずだ。そのときはRustを検討してみてほしい。2つの言語は互いをうまく補完し合うからだ。もしハンズオンのRustコンサルティングに興味があれば、カレンダーから日付を選んで、どのようにお手伝いできるか話し合おう。

結局のところ、RustとGoは本当の意味では競合ではない

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント