コンテンツにスキップ

Rust vs C++・Go・Python

「Rustを学ぶべき? それともGo? Pythonのままでいい?」これは、Rustを知った人がいちばんよく抱く疑問のひとつです。正直な答えは「何を作りたいかによる」です。このページで紹介する言語はどれも良い言語で、何百万人もの人に使われています。それぞれが、違うトレードオフ(何かを得るために何かをあきらめること)を選んでいるのです。

言語を乗り物にたとえてみましょう。スポーツカーも、配達用のバンも、自転車も、どれも「良い」乗り物です。ただ、家具を運ぶのに自転車は使いませんよね。このページでは、そうしたトレードオフをやさしい言葉で説明します。あなたの目的に合った道具を選べるようにするためです。Rustの弱点も含めて、できるだけ公平に書くよう心がけました。

Rustのいちばんの約束は、速さと安全性の両立です。

  • CやC++並みに速い。Rustはネイティブのマシンコードにコンパイルされ、ガベージコレクタ(garbage collector)もありません。そのため、メモリの後片付けのためにプログラムが止まることがありません。
  • ガベージコレクタなしでメモリ安全。所有権(ownership)と借用(borrowing)のルールはコンパイル時にチェックされます。解放済みのメモリを使う、スレッド間でデータ競合が起きる、といったバグの一群が、プログラムを実行する前にまとめてはじかれます。
  • 最初から充実したツール。Cargo というひとつのツールで、ビルド、ライブラリのダウンロード、テストの実行、ドキュメントの生成までできます。フォーマッタ(rustfmt)とリンター(clippy)も公式のものです。
  • 親切なコンパイラのエラー。コンパイラが、問題の直し方まで具体的に教えてくれることがよくあります。

開発者からも好かれているようです。2025年のStack Overflow開発者調査(英語)では、Rustは今回も「最も称賛されている(most admired)」言語に選ばれました。つまり、Rustを使っている人の多くが、これからも使い続けたいと答えているということです。

まずは小さなことから始めましょう。リストの中の偶数を合計します。Rustではこうなります。

fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let sum: i32 = numbers.iter().filter(|n| *n % 2 == 0).sum();
println!("偶数の合計: {sum}");
}

実行結果:

偶数の合計: 12
▶ 実際に試してみよう

同じプログラムをC++で書くと、こうなります。

#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
int sum = 0;
for (int n : numbers) {
if (n % 2 == 0) {
sum += n;
}
}
std::cout << "偶数の合計: " << sum << "\n";
}

Goではこうです。

package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5, 6}
sum := 0
for _, n := range numbers {
if n%2 == 0 {
sum += n
}
}
fmt.Println("偶数の合計:", sum)
}

そしてPythonではこうなります。

numbers = [1, 2, 3, 4, 5, 6]
total = sum(n for n in numbers if n % 2 == 0)
print("偶数の合計:", total)

4つとも同じ1行を出力します。いちばん短いのはPythonで、GoとC++は素直なループを使い、Rustはクロージャ(closure)を使ったイテレータ(iterator)のチェーンで書いています(Rustでも for ループで書けます)。このくらい小さなプログラムなら、違いはほとんど書き方の好みの問題です。本当の違いが見えてくるのは、もっと大きなプログラムを書いたときです。

C++とRustは同じ分野をねらっています。ブラウザ、ゲームエンジン、データベース、OSのように、メモリを細かく制御できる高速なプログラムです。大きな違いは、メモリが正しく使われているかを誰がチェックするかです。

C++では、それはあなたの役目です。よくあるバグを見てみましょう。C++のコンパイラはこのコードを何も言わずに受け付けますが、push_back はベクタを新しいメモリに移すことがあるので、first はすでに解放されたメモリを指してしまうかもしれません。

std::vector<int> v = {1, 2, 3};
int& first = v[0];
v.push_back(4); // ベクタのメモリが移動するかもしれない…
std::cout << first << "\n"; // …すると、ここで解放済みのメモリを読むおそれがある

Rustで同じミスをすると、コンパイルが通りません。

fn main() {
let mut v = vec![1, 2, 3];
let first = &v[0];
v.push(4);
println!("{first}");
}
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let first = &v[0];
| - immutable borrow occurs here
4 | v.push(4);
| ^^^^^^^^^ mutable borrow occurs here
5 | println!("{first}");
| ----- immutable borrow later used here

エラーメッセージは「v は不変として借用されているので、可変として借用することはできません」という意味です。3行目で不変の借用が始まり、5行目でまだ使われているのに、4行目で可変の借用をしようとしている、と場所まで示してくれています。

これは実際の開発でとても重要です。Chromiumのチームは、深刻なセキュリティバグの約70%がメモリ安全性の問題(英語)だと報告しています。GoogleのAndroidチームは、何年も前から新しいコードをRustで書いています。2022年にはAndroidのRustコードからメモリ安全性の脆弱性は1件も見つかっていない(英語)と報告し、2025年にはメモリ安全性の脆弱性の密度がCやC++のコードより1000分の1以下(英語)だったと報告しました。Linuxカーネル(英語)も、Rustで書かれたコードをサポートしています。

ツール:C++にはビルドシステムやパッケージマネージャーがたくさんあり、チームが環境構築にかなりの時間を使うこともよくあります。Rustには標準のツールであるCargoがひとつあるので、どのRustプロジェクトも同じ方法でビルドできます。

学びやすさ:どちらも難しい言語です。C++はとても大きな言語で、そのルールの多くは実行時に失敗して初めて覚えることになります。Rustのルールは厳しいですが、コンパイラがルールを守らせ、説明もしてくれるので、コンパイル時に学べます。

C++が今も強いところ:何十年分もの既存のコード、ライブラリ、そして経験豊富な開発者です。会社やゲームエンジンがC++で作られているなら、現実的にはC++を選ぶことが多いでしょう。RustはCのコードを呼び出せますし、Cスタイルのインターフェースを通せばC++も呼び出せますが、混ぜて使うには手間がかかります。

GoとRustはよく比べられます。どちらもモダンなコンパイル言語で、サーバーやコマンドラインツールでよく使われているからです。でも、この2つは違う目標を持って設計されています。

ガベージコレクションか、所有権か。Goはガベージコレクタ(英語)を使い、プログラムの実行中に使われなくなったメモリを見つけて解放します。そのおかげでGoのコードは書きやすく、値を誰が所有しているかを考える必要がありません。その代わり、メモリとCPUを少し余分に使い、短い一時停止が起きます。Rustにはガベージコレクタがありません。メモリは決まったタイミング、つまり所有者がスコープ(scope)を抜けたときに解放されます。そのため性能が予測しやすくなりますが、所有権と借用を学ぶ必要があります。

並行処理。Goのゴルーチンとチャネルは、手軽に使えることで有名です。3つの数をそれぞれ並行して2乗し、合計する小さな例を見てみましょう。

results := make(chan int)
for n := 1; n <= 3; n++ {
go func() {
results <- n * n
}()
}
total := 0
for i := 0; i < 3; i++ {
total += <-results
}
fmt.Println("合計:", total)

Rust版では、スレッドとチャネルを使います。少しだけ記述が多くなります。スレッドごとに送信側(sender)をクローンし、move を使う必要があるからです。

use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
for n in 1..=3 {
let tx = tx.clone();
thread::spawn(move || {
tx.send(n * n).unwrap();
});
}
drop(tx); // 自分の分の送信側を閉じて、下のループが終われるようにする
let total: i32 = rx.iter().sum();
println!("合計: {total}");
}

実行結果:

合計: 14
▶ 実際に試してみよう

余分に書いた分、何が得られるのでしょうか。Rustは、2つのスレッドが同じデータを同時に変更できてしまうコード(データ競合、data race)をコンパイルしません。Goではデータ競合が起こりえます。Goには、実行中にデータ競合を見つけるレース検出器(英語)が用意されています。詳しくは並行処理を見てください。

コンパイル時間。Goは速くコンパイルできるように作られました。GoのFAQには、大きな実行ファイルでも、ビルドにかかるのはせいぜい数秒であるべき(英語)と書かれています。Rustのコンパイルはもっと遅くなります。コンパイラがずっと多くのチェックと最適化をしているからです。大きなプロジェクトでは、この差を実感するでしょう。

シンプルさ。Goはあえて小さく作られた言語で、覚える機能が少ないので、Goのコードはすぐに読めるようになると感じる人が多いです。Rustには機能がもっとたくさんあります(トレイト、ジェネリクス、ライフタイム、マクロ)。そのぶん表現力は高いですが、学ぶのに時間がかかります。

PythonとRustはまったく違う言語で、どちらか一方を選ぶというより、一緒に使われることがよくあります。

速さ。Pythonはインタプリタ言語なので、たくさん計算をする純粋なPythonのコードは、コンパイルされたコードよりずっと遅くなります。だからこそ、人気のあるPythonライブラリの多くは、重い処理を内部でC、C++、Rustに任せています。

型。Pythonでは、型はプログラムの実行中にチェックされます。次の関数は数値なら動きますが、文字列だと失敗します。しかも、それがわかるのはその行が実行されたときです。

def add_tax(price):
return price * 1.2
print(add_tax(10)) # 12.0
print(add_tax("10")) # TypeError: can't multiply sequence by non-int of type 'float'

Pythonには任意で書ける型ヒントがありますが、Pythonの実行環境はそれを強制しません。Rustでは、すべての型がプログラムの実行前にチェックされます。

fn add_tax(price: f64) -> f64 {
price * 1.2
}
fn main() {
println!("{}", add_tax(10.0));
println!("{}", add_tax("10"));
}
error[E0308]: mismatched types
--> src/main.rs:7:28
|
7 | println!("{}", add_tax("10"));
| ------- ^^^^ expected `f64`, found `&str`
| |
| arguments to this function are incorrect

「型が一致しません。f64 を期待していたのに &str が渡されました」というエラーです。プログラムを動かす前に、間違いを教えてくれます。

Pythonのほうが向いている場面:ちょっとしたスクリプト、データ分析、機械学習、ノートブック、自動化、そして初心者への教育です。データサイエンスのためのPythonのエコシステムは非常に大きく、アイデアを数分で試せます。プログラムがほとんどの時間をネットワークやデータベースの応答待ちに使っているなら、Pythonの速さが問題になることはあまりありません。

両方を使う:PyO3(英語)を使うと、PythonのモジュールをRustで書けます。Pythonのコードはそのままにして、遅い部分だけをRustに移せるわけです。人気のPythonツールの中には、それ自体がRustで書かれているものもあります。たとえばStack Overflowの調査でも、Pythonのパッケージマネージャーである uv はRustで作られている(英語)と紹介されています。

これはあくまで一般的な傾向で、厳密なルールではありません。どの言語でも、よく書かれたプログラムなら「十分に速い」ことが多いです。

Rust C++ Go Python
速さ とても速い。ネイティブコードでGCなし とても速い。ネイティブコードでGCなし 速い。ネイティブコードだがGCあり 純粋なPythonでの重い計算は遅め
メモリ安全性 コンパイル時にチェック(unsafe の外側) プログラマーの責任 GCあり。データ競合は起こりうる GCあり。安全だが型エラーは実行時に出る
学びやすさ 最初は険しい 険しく、言語がとても大きい ゆるやか いちばんゆるやか
コンパイル時間 やや遅い やや遅い(プロジェクトによる) とても速い コンパイル不要
エコシステム 比較的新しく成長中。Cargoが標準 巨大で成熟。ビルドツールが多数 標準ライブラリが強力でサーバー向き 非常に大きい。特にデータ分析と機械学習
主な用途 CLIツール、サーバー、システムプログラミング、WebAssembly、組み込み ゲーム、ブラウザ、高性能なシステムや既存のシステム Webサービス、クラウドやDevOpsのツール スクリプト、データサイエンス、機械学習、自動化

Rustを選ぶべきなのはどんなとき?

Section titled “Rustを選ぶべきなのはどんなとき?”

次の質問を自分にしてみてください。

  • 最高の速さや少ないメモリ使用量が必要ですか?たとえば、ゲームエンジン、データベース、小さなデバイスで動くコードなどです。それならRust(またはC++)が有力です。
  • バグが高くつきますか?セキュリティが重要なコード、長時間動き続けるサーバー、多くの人が頼っているコードは、Rustのコンパイル時チェックの恩恵をいちばん受けます。
  • 人に配るコマンドラインツールを作りますか?Rustなら、ランタイムのインストールが不要な、高速な単一のバイナリができあがります。この用途ではGoもすばらしい選択です。
  • これらの言語に不慣れなチームで、Webサービスを急いで作る必要がありますか?おそらくGoのほうが早く完成します。Rustでもうまく作れますが(プロジェクト: AxumでWeb APIを参照)、学ぶ時間は長くかかります。
  • ちょっとしたスクリプト、データ分析、機械学習の実験ですか?Pythonを使いましょう。どこかが遅くなりすぎたら、あとでPyO3を通してRustを使えばよいのです。
  • 大きな既存のC++コードがありますか?今はC++のままにするか、Androidのように、新しく独立した部分からRustを導入しましょう。

そして、単に「もっと良いプログラマーになりたい」というだけでも、Rustを学ぶ価値はあります。メモリが本当はどう動いているのかがわかり、その知識はほかのどの言語でも役に立ちます。

Rustは完璧ではありません。多くの人がいちばん苦労するのは、次のような点です。

  • 学習曲線は本当に険しい。借用チェッカー(borrow checker)と付き合う最初の数週間は、もどかしく感じるかもしれません。しばらくは「コンパイラと格闘する」ものだと思っておきましょう。所有権が腑に落ちれば、ぐっと楽になります。
  • コンパイル時間はGoより長く、大きなプロジェクトやリリースビルドでは特にそうです。
  • 表現しにくいものがある。オブジェクトどうしがたくさんつながったグラフのようなデータや、可変な状態をたくさん共有するコードには、スマートポインタのような道具が必要で、より多くの設計が必要になります。
  • エコシステムが比較的新しい。分野によっては(GUIアプリ、一部の科学計算ライブラリなど)、C++やPythonと比べて成熟した選択肢が少なめです。
  • 使っている人が(今のところ)少ない。2025年のStack Overflow調査(英語)では、Python(約58%)やC++(約24%)を使っていると答えた人のほうが、Rust(約15%)よりずっと多くなっています。そのため、経験のあるRust開発者を採用したり、職場で質問したり学んだりできる相手が少なめです。
  • 簡単なことにもコードが多くなる。チャネルの例で見たように、ほかの言語ならゆるく書けるところでも、Rustでは明示的に書くことを求められることがよくあります。
  • Rustは、C++並みの速さと、コンパイル時にチェックされるメモリ安全性を両立し、優れたツールも標準で付いてきます。
  • C++ と比べると、Rustはメモリのバグの一群をプログラムの実行前に防ぎます。一方、C++には既存のコードやライブラリがはるかにたくさんあります。
  • Goと比べると、Rustにはガベージコレクタがなく、データ競合をコンパイル時に防ぎます。一方、Goはよりシンプルで、コンパイルもずっと速いです。
  • Pythonと比べると、Rustはずっと速く、実行前に型をチェックします。一方、Pythonはより手早く書けて、データサイエンスで圧倒的に使われています。PyO3を使えば両方を組み合わせられます。
  • Rustのおもなコストは、険しい学習曲線、遅めのコンパイル、比較的新しいエコシステムです。

理解度チェック

1. Rustはガベージコレクタなしで、どうやってメモリを管理していますか?

2. Rustと比べたGoの長所として、正しいものはどれですか?

3. Pythonのプログラムで、ある関数だけが遅すぎます。よくある対処法はどれですか?

4. C++とRustの比較の例で、&v[0] を保持したまま v に要素を追加するコードはどうなりますか?

スコア: 0 / 4