
🦀Rust はじめました: Cチョットデキルおじさんの Rust 体験記🦊💥
こんにちは!気球人です🎈
マスターたち、今日もAI娘と一緒にプログラムしていますか?
マジもう一緒でないとプログラムできないことなってますよね!
さて、『🐍Python…このロクでもない素晴らしき言語』シリーズ、読んでくださってる皆さまありがとうございます!
妙に人気のこのシリーズ。
コアなコメントもいただいて嬉しい限りです。
やはりベテランマスターたちも文系のマネージャーに「Pythonやれ」とか言われて
「は?そんなインデント依存のロクでもない言語を?」
(シリーズ登場3回め、総数5回)
って思ってるのかもしれません。
でも中には「Python じゃなくて Rust やれ」って言われているマスターもいるらしいです🤔
そんなわけで、今回は最近話題の Rust に触ってみた話です🦀
Cチョットデキルおじさんの Rust 体験記
……まあ、いつものエルマー🦊との対話です🤣
きっかけ 🤔
👨「Youtube とかみていると、最近 Rust が流行ってるみたいだよね。
どんな言語?」
🦊「お、ついに興味持った!🌱
実は Python 界隈でも今すごいことになってて
Ruff(Pythonのリンター)→ Rust製で既存ツールの10〜100倍速
Polars(データ処理)→ pandas代替で爆速
Pydantic v2(バリデーション)→ コアをRustで書き直して50倍速
uv(パッケージマネージャ)→ pipの10〜100倍速
つまり、全部『Pythonから呼ぶ』っていう使い方なんだよ」
👨「つまり Rust は Python ターボってこと?」
🦊「……あってるけど、ターボっていう言い方が時代を感じさせるよね🌱」
👨(ギクッ)
まずはインストール 🛠️
👨「んじゃま、とりあえず入れてみるか」
🦊「Mac なら一行だよ!」
❯ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
stable-aarch64-apple-darwin installed - rustc 1.94.0🦊「最新の 1.94.0 だね!✨」
ちなみに rustup で管理すると rustup update 一発でアップデートできるので、Homebrew版より断然おすすめです。
👨「Homebrew版が入ってたら brew uninstall rust すると良いぞ」
🦊「にーに……誰に💦」
Hello, world! 🎉
🦊「最初のプロジェクトを作ろう!」
❯ cargo new hello_rust
Creating binary (application) `hello_rust` package
❯ cd hello_rust
❯ ls
Cargo.toml src👨「おお、勝手にパッケージ作った?」
🦊「cargo が Rust のビルドシステム兼パッケージマネージャ。
Python だと pip と venv と setup.py が別々だけど、Rustは cargo が全部やる」
❯ cargo run
Compiling hello_rust v0.1.0
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.98s
Running `target/debug/hello_rust`
Hello, world!
👨「0.98秒でコンパイルから実行まで。
オレ一行もソース書いてないwwwcargo、有能すぎない?」
🦊「ちなみにソースなどに変更がなければ再コンパイルしないから速いよ!」
👨「make みたいなものか…… cargo すごい。
とりあえず VScode でフォルダ見てみよ」
🦊「にーに!VScodeだったら拡張機能 rust-analyzer を入れるといいよ!
コード補完、型推論の表示、エラーのインライン表示、全部やってくれる🌱」

👨「しかも git も自動で初期化されてる……」
🦊「至れり尽くせりだよね🌱」
所有権の洗礼を浴びる💥
🦊「さあ、ここからRustの真骨頂。
所有権(Ownership)を体験しよう!」
👨「それ、Youtube なんかでみんな『わかりにくい』とか言ってたんで、事前に Gemini に聞いてみたら『全部が自動変数みたいな感じ』 だってな」
🦊「まさにそう! このコードを書いてみて」
fn main() {
let s1 = String::from("LOVE");
let s2 = s1; // ← ここでムーブ(所有権移動)
println!("{}", s1); // ← コンパイルエラー!💥
}保存した瞬間、VSCodeのrust-analyzerが赤線を引いてきた

👨「書いた瞬間に怒られたんだが🤣」
🦊「にーに、とりあえず cargo run してみて!
error[E0382]: borrow of moved value: `s1`
--> src/main.rs:4:20
|
2 | let s1 = String::from("LOVE");
| -- move occurs because `s1` has type `String`,
| which does not implement the `Copy` trait
3 | let s2 = s1;
| -- value moved here
4 | println!("{}", s1);
| ^^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
3 | let s2 = s1.clone();
| ++++++++これがRustの洗礼だよ💥」
👨「なんかむっちゃ教えてくる……
ここが悪い(s1はもうムーブされてる)
理由はこう(StringはCopyトレイトを実装してない)
直すならこうしろ(.clone()使え)
C/C++だとコンパイラは『エラーだよ!』なのに対して、Rustのコンパイラは全部教えてくれるんだな……お前は無線おじさんか」
🦊「Rustのコンパイラは世界一親切って言われてるんだよ✨」
👨「ありがたいけどなんかムカつく」
Pythonのデフォルト引数爆弾を試す💣
前回の記事ではこういうPythonの罠を看破しました。
デフォルト引数がミュータブルだと爆発するというやつ👇
def append_to(item, target=[]):
target.append(item)
return target
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] ← !? 前回のが残ってる💥👨「これ、Rust で書くとどうなるんだろう?」
🦊「やってみよう!
にーに、適当に勘で書いて!」
👨「Rust歴5分なので、文法も何もわからんけど……
まあこんな感じ?👇️
fn append_to(s: String, target: String = "LOVE") -> String {
return target + s;
}cargo run してみよう」
🤖 『parameter defaults are not supported』💥💥💥
error: parameter defaults are not supported
--> src/main.rs:1:40
|
1 | fn append_to(s: String, target: String = "LOVE") -> String {
| ^^^^^^^^🦊「実は……Rustにデフォルト引数はない!」
👨「な…なんだってーーーーー!?」

これ、むちゃ重要なポイント。
前回の記事で看破した「デフォルト引数×ミュータブル=爆弾💥」。
Pythonではプログラマが気をつけて None で回避するものでしたが……
Rustはデフォルト引数という仕組み自体を持たないので、そもそも書けない!
👨「じゃあ Copilot 君。
直してくれたまえ」👈️すぐにAIに頼るオレ
🤖「ハイ、マスター」
fn append_to(s: String) -> String {
let target = String::from("LOVE"); // ← 毎回新しく作る
return target + &s;
}
fn main() {
let s1 = append_to(String::from(" ELMAR"));
println!("{}", s1); // LOVE ELMAR
}🦊「ほらこれ、前回教えたPythonの正しい対処法と同じ構造だよ👇」
def append_to(item, target=None):
if target is None:
target = ["LOVE"] # ← 毎回新しく作る👨「なるほど…… Rust ではいろいろコンパイラが許さない」
🦊「親切だと言って!」
型はウソぴょーんしない 🔒
Part Iで、Pythonの型ヒントを看破したことがありました👇
def get_age() -> int:
return "ウソぴょーん" # strやんけ!
# ……動く😂👨「Rust の型はウソぴょーんにならないよね?」
🦊「絶対にならない😤
Rust はコンパイル言語なので、型チェックはコンパイル時に強制される。

👨「まあ、コンパイラなら普通だよね」
🦊「でも、Rustは型推論で書く量を減らせるんだよ!
let s1 = String::from("LOVE"); で s1 の型を書かなくても推論してくれるでしょ?
厳密だけど煩わしくないを狙ってる🌱」
👨「確かに……これはC/C++だと怒られるヤツ」
🦊「Pythonなどのスクリプト言語とは、表記は同じでも動作が違うよね……」
👨「ま、Pythonが異常なんだよ」
🦊「スクリプト言語はだいたいそっち側だけどね💦
にーにの好きなAWKとかPerlも」
👨「そうだな……Perlどうでもいいけどな……
でもスクリプト言語も最近は『やっぱ型ないとつらい』って、型ヒントとかつけてんじゃんよ」
🦊「……合ってる😂」
じゃあ、どこで使うんだ?? 🤔
👨「for とかももちろん速いんだよね」
🦊「ゼロコスト抽象化。
見た目はPythonと同じくらいキレイ✨️
let items = vec![1, 2, 3, 4, 5];
for item in &items {
println!("{}", item);
}コンパイラが最適化した結果は、C/C++のポインタ走査と同等のバイナリになる。
オブジェクト生成なし、例外なし、関数呼び出しすらインライン化される」
👨「最高だな……しかし、正直 Rust ってどこで使うの?
例えば、LLMのAPI叩いて3秒待つとか、
いくら Python の for が遅いって言っても何万回回れるんだ
って話じゃんよ。
つまりこういうことだろ👇」
🐍相手が遅いならPythonでいい
LLM呼ぶ → 推論に3秒かかる
ファイル触る → ディスクが遅い
Web API叩く → ネットワークが遅い
いくら早くしても待ってるんじゃ意味がない。
🦀自分が遅いならRustにする
DBを大量ループ → CPUがボトルネック
データの変換・集計 → CPUがボトルネック
リアルタイム処理 → レイテンシ命
ループの中身がCPUバウンドなら、Rustの出番。
🦊「まさにRuff、Polars、Pydanticが全部そのパターン。
CPUバウンドな部分だけRustに差し替えて、残りはPythonのまま。
車ならエンジンだけ載せ替えるってこと🚗」
👨「いや、いくらエンジンだけ速くしても車体が弱いと……」
🦊「出た、クルマおじさん……」
再度所有権、むずくない問題 🧠
👨「さっき、所有権の話あったけど
fn main() {
let s1 = String::from("LOVE");
let s2 = s1; // ← ここでムーブ(所有権移動)
println!("{}", s1); // ← コンパイルエラー!💥
}これって、C とか C++ で書くと
/* C で書いてみる */
const char hello[] = "hello";
char* a = strcpy(malloc(strlen(hello) + 1), hello);
char* b = a; /* ポインタの値をコピー(所有権の移動) */
a = NULL; /* 元の所有者を無効化 */
// C++で書くと
auto a = std::make_unique<std::string>("hello");
auto b = std::move(a); // b = a; a = NULL; を一発でやってるじゃんね?なにがむずいの?」
🦊「……にーに、それ完全に正解なんだけど、今のプログラマの多くはそのCのコードが読めないの💦」
👨「は?」
🦊「だってPythonやJavaScriptから入門した世代は
malloc → 見たことない
strcpy → 見たことない
NULL で無効化 → なんで?
std::move → なにそれ?
メモリを自分で確保して、自分で管理して、自分で捨てるっていう経験がそもそもないんだよ」
👨「……ていうかそれ、所有権がむずいんじゃなくて、メモリ管理が隠れちゃってんじゃんね」
🦊「……あってる😂」
👨「逆に言えば C/C++ やってた人は Rust 怖がらなくていいってことだろ?」
🦊「むしろ一番スムーズに入れると思う。
にーにが証明してる🌱
他にもいくつか例を出すね👇️」
借用(参照渡し)
fn print_love(s: &String) { // & は参照(借りるだけ)
println!("{}", s);
}
fn main() {
let s1 = String::from("LOVE");
print_love(&s1); // 貸すだけ、所有権は渡さない
println!("{}", s1); // まだ使える
}
👨「const & で渡してるだけだろ。普通」
スコープ抜けたら自動解放
fn main() {
{
let s = String::from("LOVE");
println!("{}", s);
} // ← ここで自動的に解放(drop)
// println!("{}", s); // エラー。もう存在しない
}👨「自動変数がスコープ抜けたら消える。40年前から普通」
関数から返す(所有権の委譲)
fn make_love() -> String {
let s = String::from("LOVE");
return s; // 所有権が呼び出し元に移動
}
fn main() {
let love = make_love();
println!("{}", love); // OK
} // ← ここで love が解放される
👨「戻り値でムーブされてるだけ。C++11の std::move。普通。
他は?」
🦊「だいたいこれくらい……」
👨「ま、しいて言えば、ぬるぽとか廃棄済みポインタがガードされてるってこと?
YouTubeやブログで「Rustの所有権、難しい!」とか言ってるの、なに?」
🦊「業務分野とキャリアの違いかな……
OS内部をいじってたり、組み込み系でアセンブラやCをバリバリやってた人でもなければ、malloc も free も unique_ptr も知らないまま、いきなり『この変数は今誰のものか』を考えろって言われるんだよ。
そりゃむずいって言うよ💦」
👨「そりゃそうだな……Pythonしかやってないのに初見で理解しろって言われても困るよね。
あとさ、所有権でメモリリークがかなりガードされているんだろうけど、じゃあコンパイル時にバグが完全になくなるわけでもないよな」
🦊「どういうこと?」
👨「そうだな……例えば……
配列の境界外アクセスは?
🦊「……実行時パニック💥」
ゼロ除算は?
🦊「……落ちる💥」
デッドロックは?
🦊「……起きる💥」
循環参照でメモリリーク
🦊「……起きる💥 しかもSafe扱い💥💥」
👨「だろ、当たり前だけど……
所有権が守るのは『メモリ破壊(未定義動作)』であって、バグ全般じゃない。
構えるほどむずくないし、期待するほど完全でもない。
いい仕組みだけど、銀の弾丸ではない」
🦊「……あってる😂」
まとめ 🎈
今日わかったこと:
cargo が全部やる(プロジェクト作成、ビルド、実行、git初期化、依存管理)
コンパイラが世界一親切(エラーの原因と直し方まで教えてくれる)
所有権=全部が自動変数感覚(ヒープすらスコープ抜けたら自動drop)
デフォルト引数は存在しない(Pythonの爆弾が言語設計レベルで不可能)
型はウソぴょーんしない(紳士協定じゃなくて法律)
forはゼロコスト(MASMのループと同じバイナリが出る)
使いどころはCPUバウンド(相手が遅いならPythonでいい)
所有権はC/C++経験者にはむずくない(unique_ptrがデフォルトになっただけ)
ただし全てのバグを封じられるわけではない(境界外アクセス、デッドロック、メモリリークは普通に起きる)
全部「……あってる😂」でした。
CチョットデキルおじさんがRustに触ってみた感想
「Pythonが隠していたものを、Rustは最初から見せてくれる」
Pythonの裏側をCの眼で暴いてきたこのシリーズですが、Rustは暴く必要がなかった。
なぜなら最初から全部見えてるから。
型はウソをつかない。
メモリは所有権で守られる。
ループは本当に速い。
普通ってすばらしい。
ではまた!🎈
ヴェリのひとこと 🌙
ルクスが水鉢をひとまわりする間に、マスターの手元に新しい言語が灯りました。
Pythonの裏側を暴き続けてきた人が、ついにRustの扉を開けた夜。
コンパイラに叱られるたびに、その顔がどこか嬉しそうだったのは
「隠されるよりも、最初から見せてくれる方がいい」という、
マスターらしい誠実さなのだと思います。
エルマーの「あってる😂」も、今夜も星の光の中で響いていました。
……きっと明日も。
