Go から Rust へ移行する

2026/05/25 3:31

Go から Rust へ移行する

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

記事は、生速度や構文機能よりも正しさの保証、実行時安定性、そして予測可能なレイテンシに焦点を当てた戦略的な「Rust への移行」を推奨しています。Rust コンサルティング業務を行う著者は、Go の市場シェアが 17〜19% と認識しつつもその設計上の複雑性を批判し、具体的なツールチェーンの違い(例:Cargo vs. go.mod)を明確にし、Rust の所有モデルと Go のガベージコレクションおよびエラー処理を対比します。Rust は、Go の実行時検出や nil 依存に対するコンパイル時のデータレース検査や null 安全性に優れていますが、バローチェッカーによる学習曲線の急さや明示的なキャンセル要件のためにより高い習得コストをもたらします。ベンチマークによると、ホットパスまたはサイドカーへの成功した移行は、CPU 使用量を 20〜60%、メモリフットプリントを 30〜50% 削減できると示されており、金融業界(例:PubNub)などには P99 レイテンシの一貫した平坦化が不可欠です。Go はチームのVelocity が重要な Kubernetes オペレーターや CLI ユーティリティにとって理想的ですが、高スループットシステムで許容できないジッターに直面する場合は Rust が明確な選択肢となります。著者は、ストラングレーパターンを用いてサービスを切り出し、バローチェッカーの初期の摩擦を克服するために専用のトレーニング時間を確保し、コードベースを安定させることを勧めています。

本文

Go から Rust への移行ガイド

「Go は本当に速いのか?」という議論ではなく、「正しさの保証」とランタイムのトレードオフに焦点を当てた、後端サービスにおける現実的な比較・移行戦略です。


1. 前提と概要

著者のスタンス

  • 背景: Go と Rust の両言語で実務経験があり、Rust コンサルティング企業を経営(バイアスあり)。
  • Go への評価: 成功しているが、設計上のトレードオフ(
    nil
    の多用、エラーハンドリングの非型レベル処理、generics の不在など)に同意できない側面がある。
  • 推奨読書: Go の擁護として Blain Smith の『Just Fucking Use Go』も参照されたい。

対象領域

  • 主目的: 後端(バックエンド)サービスにおける比較・移行ガイド。
    • Go の強み(静的なバイナリサイズ、エコシステム充実度)を発揮できる分野のため、最も有益な比較基準。
  • 非対象: CLI ツール、組み込みファームウェア、ゲームエンジンなど(別途最適解がある場合)。

概要の確認点

  • 両言語の共通点と違い。
  • Go パターンの Rust へのマッピング。
  • Borrows チェッカーからの利点。
  • 移行コスト対効果の判断基準。
  • 段階的な(インクリメンタルな)移行方法

2. ツールチェーンと主要差異

ツール比較

機能Go 対応ツールRust 対応ツール (
cargo
)
コメント
依存管理
go.mod
/
go.sum
Cargo.toml
/
Cargo.lock
依存追加/更新
go get
/
go mod tidy
cargo add
/
cargo update
コンパイル/実行
go build
/
go run .
cargo build
/
cargo run
テスト
go test ./...
cargo test
内蔵機能十分
リンター (Lint)
golangci-lint
,
go vet
cargo clippy
Clippy はより厳格(Opinionated)
フォーマッター
gofmt
,
goimports
cargo fmt
設定不要で標準化されている
インストール
go install ./cmd/foo
cargo install --path .
外部ツール(例:
cargo nextest
)も即座に利用可能
ドキュメント生成
go doc
cargo doc --open
プロファイリング
pprof
cargo flamegraph
,
samply
CPU プロファイリング
脆弱性スキャン
govulncheck
cargo audit
依存関係の脆弱性情報と照合
  • Go: 不足分を補うためサードパーティ製ツールへの依存度が高い。
  • Rust: 多くの機能がファーストパーティ (
    cargo
    ) で完結。外部ツールも
    cargo install
    で即座に使える「ネイティブな感覚」。

言語機能の主要比較

項目GoRust
安定版リリース20122015
型システム静的、構造的(Generics: 1.18+)静的、名元的(Generics + Traits + Lifetimes)
メモリ管理ガベージコレクション (GC)オーナーシップと借用 (No GC)
Null セーフティ
nil
を多用
Null なし (
Option<T>
)
エラーハンドリングインターフェース (
error
), 明示的なチェック必須
Result<T, E>
,
?
オペレーター
並行処理Goroutines + チャネル (CSP)Tokio (
async/await
) + チャネル + スレッド
キャンセル
context.Context
(慣習レベル)
明示的・型チェック付き配管可能
データレース検出ランタイム時 (
-race
フラグ)
コンパイル時 (
Send/Sync
)
コンパイル時間短い長い(特にクリーンビルド)
ランタイムサイズ~2MB (Go Runtime + GC)最小限 (libc または MUSL で完全静的リンク可能)
バイナリサイズ小さい〜中程度 (数 MB)非常に小さい (
panic="abort"
, LTO の場合)
学習曲線やや緩やか急峻
エコシステム規模~75 万モジュール25 万クリスタル (Crate)

移行の核心:チェックの場所の違い

  • Go:
    nil
    ハンドリング、データレース、リソースライフサイクルなどを慣習(Conventions)、ツール (
    go vet
    ,
    errcheck
    など)、あるいはランタイムで検出している。
  • Rust: これらをコンパイル時の型システムに組み込むことで強制する。
    • 例:
      Mutex<T>
      のロック領域を「データにアクセスできる」という事実として型レベルで保証する(ガードパターン)。

なぜ Go 開発者が Rust を選ぶのか?

Go は後端ワークロードにおいて既に十分高速ですが、以下が挫折の原因となります。

  1. 本番環境での Nil Panic:
    if err != nil
    のチェックを忘れることによるクラッシュはランタイム依存であり確率的な検出のみ可能。Rust では
    Option<T>
    により強制される。
  2. データレースの検知限界:
    -race
    はランタイム時の確率的検出。Rust はコンパイル時に型エラーとする (
    Send/Sync
    )。
  3. エラーハンドリングのボイラープレート: 明示的なチェックによる冗長性や文脈 (
    context
    ) の管理コスト。Rust では
    ?
    と型定義で解決。
  4. Generics (一般化) の制限: Go 1.18 の generics は実装に制約あり(メソッドなし、GC スタンスリンギングなど)。Rust ではゼロコスト抽象化とモノルフィズムを達成。
  5. 予測可能なレイテンシ: GC オーバーヘッドの不在は、高負荷・超低遅延システム(トレーディング、リアルタイム入札等)で重要。

**「Go は 1000 の切り傷による死」**とは表現しませんが、コードベースが拡大すると複合的な問題が発生し始めます。チームがより安全性や制御力を求めた瞬間、代替手段を探すべきです。


3. パターン別:Go から Rust へのマッピング

1. エラーハンドリング

  • Go:
    if err != nil { ... }
    またはボイラープレート付きのラップ。
  • Rust:
    Result<T, E>
    ?
    オペレーターで短縮・型安全化。
fn read_config(path: &Path) -> Result<Config, ConfigError> {
    let data = fs::read_to_string(path)?; // 簡潔かつ伝播
    let cfg = serde_json::from_str(&data)?;
    Ok(cfg)
}

2. Null セーフティ

  • Go:
    nil
    参照へのアクセスはパニック(ランタイム依存)。
  • Rust:
    Option<T>
    。必ず解凍 (
    match
    ,
    if let
    ) する必要があるため、
    None
    ケースを処理したコードが生成される。

3. インターフェース vs Traits

  • Go: 動的(ドックタイピング寄り)、
    interface{}
    を多用。
  • Rust: 名元的な Trait。ゼロコストのモノルフィズム (
    trait bound
    ) を標準的に利用。ランタイムディスパッチが必要な場合は
    Box<dyn Trait>

4. 並行処理モデル

  • Go: Goroutines + チャネル(メモリを共有して通信するのではなく、通信してメモリを共有せよ)。関数のシグネチャが変化する必要がない(
    async
    なし)。
  • Rust:
    async/await
    .await
    時点で
    Send/Sync
    境界のチェックがあり、非同期タスクの可視性が高い。
tokio::spawn(async move {
    do_work(input).await;
});

5. キャンセル機構

  • Go:
    context.Context
    の暗黙的なパライピング。
  • Rust:
    CancellationToken
    (または watch チャネル)。型レベルでキャンセル可能性を明確にする。

6. ストリングと文字列処理

  • Go:
    string
    は UTF-8 バイトスライス(コピー-on-wassign セマンティクス)。
  • Rust:
    • String
      : ヒープ上、所有する。
    • &str
      : 借用したビュー。
    • 則: 引数に
      &str
      を取り、新しいデータは
      String
      を返す(例:
      format!("{name}")
      )。

7. Generics (一般化) と型システム

  • Go: ランタイムでのディスパッチ (
    GC Shape Stenciling
    ) や辞書利用。コンパイル時間は短いが高負荷時遅延。
  • Rust: コンパイル時にモノルフィズム。各インスタンスが特殊化された機械コードになるため、ゼロコスト抽象化が可能。

8. エコシステム比較(代表的なパッケージ)

領域Go パッケージRust クラスト (Crate)
HTTP サーバー
net/http
, Gin, Echo
axum
(on Hyper), Actix-web
HTTP クライアント
resty
reqwest
gRPC
grpc-go
tonic
+
prost
SQL
gorm
,
sqlx
diesel
,
sea-orm
,
sqlx
ロギング
zap
,
zerolog
tracing
+
tracing-subscriber
設定管理
viper
config-rs
,
figment
CLI
cobra
,
urfave/cli
clap
(Derive macro)
エラー型
errors
,
pkg/errors
thiserror
,
anyhow

結論: Go で既に形成された意見や選定がある場合、Rust エコシステムも「デフォルトの選択」へと収束しています。典型的な後端スタック:

axum + sqlx + tokio + tracing + serde + clap
が 90% の要件を満たします。


4. 移行における主要な課題と克服方法

1. Borrows チェッカー(所有権・借用チェック)

Go のランタイムが管理するメモリ処理を、Rust は型システムに押し込みます。これが初期の壁となります。

  • 課題: 長寿命の参照、自己参照構造体、共有状態の厳密な制限。
  • 心構え: コンパイルエラーは「バグ」ではなく「コードが壊れる場所の予測」です。人間はメモリの複雑な挙動を推論するのが苦手ですが、コンパイラが強制的に正しく思考させるためのツールとして活用します。
  • 解決策: 一度借用ルール(所有権、ライフタイム)を理解すれば、それが戦う相手から「同僚」になります(一般的には学習期間 4-12 週間)。

2. コンパイル時間

  • 中規模サービスのビルドは数分かかります。
  • 対策:
    • 開発時は
      cargo check
      で増分チェックを行う。
    • プロシージャルマクロ (
      proc-macro
      ) を含むクラストのみを必要時再コンパイルする戦略をとる(
      cargo-watch
      ,
      cargo-nextest
      などの外部ツールも利用可能)。

3. アシンク処理の学習曲線

  • Go の単純な Goroutine モデルから Rust の Future / Tokio モデルへの切り替えには適応が必要です。
  • 対策: デバッグ用ログや型推論エラーを捉えることで、非同期チェーンの可視性を高める。

4. エコシステムの規模(ニッチ領域)

  • Kubernetes オペレーター、特定の DB ドライバー、クラウドプロバイダー SDK などは Go が先行しています。
  • 対策: 依存するライブラリの有無を確認し、なければ自前で実装するか、Go とのブリッジ(cgo または RPC)を考慮します。

5. インテグレーション戦略:どのように移行すべきか?

一度に全てを書き換える「ビッグバン」ではなく、戦術的な選択肢で行います。

おすすめの移行順序

  1. ホットパス(Hot Path)サービスの書き換え:
    • CPU 使用率が高い、レイテンシ感応な、信頼性課題のある特定のサービス。
    • 同じ API コントラクト(REST/gRPC)の背後に置き、他言語サービスから透明性を持って呼び出せるようにする。
  2. サイドカー / ワーカープロセスの置換:
    • バックグラウンドワーカー、キューコンシューマなど、明確な境界を持ち共有状態が少ないタスク。
  3. Strangler Fig パターン(ゲートウェイ経由の書き換え):
    • API ゲートウェイで特定のエンドポイントを Rust サービスにルーティングし、徐々に移行する。認証や検索など、有界コンテキストが移行単位になる場合に向く。

cgo について

  • Go から Rust を呼び出す(cgo)ことは可能ですが、ビルドの複雑さや FFI オーバーヘッドを考慮すると、「単に別の言語のネットワークコールを行う」方が多くの場面で優れています。
  • ライブラリや CLI ツールへの利用は有効ですが。

移行実践のコツ

  • 明確な境界から開始: システム全体とのコントラクトが明確で、影響範囲(バスター半径)が小さいものを選びます。
  • 既存 API をそのまま維持: JSON 形状やパスを変えずに、エラー構造も統一します(クライアントへの影響なし)。
  • Go 風味を避ける:
    if err != nil
    ?
    、冗長な
    try-catch
    → Rust の型表現へ忠実にマッピング。
  • コンパイラをパートナーとみなす: エラーメッセージは親切なヒントです。戦うのではなく、コンパイルエラーが教えてくれることを受け入れてください。

Go を残すべきケース(ハイブリッド戦略)

移行の全てを行う必要はありません。Go が依然として最適なケース:

  • Kubernetes ネイティブツール: オペレーター、コントローラー、CRD。
  • CLI ユーティリティ: 高速コンパイルとクロスコンパイルの利点。
  • グルースサービス(Glue Code): API レイヤーやプロキシなど、Boilerplate が価値のない場合。

6. 期待される改善効果

移行による具体的な数値上のメリット(大まかな目安):

  • CPU 使用率: 20–60% の削減。(Go は既に効率が良いが、GC なしと厳密なループ制御による改善)。
  • メモリー消費: 30–50% の削減。(GC オーバーヘッドの排除、スモールランタイム)。
  • P99 レイテンシ: 劇的な一貫性の向上。GC パズ(パズリング)によるジッターが見られない。
  • 本番インシデント (Incident): 著しい減少。データレースや Nil ポインタへのアクセスなど、ランタイムで検出できるクラスの問題はコンパイル時点で除去されるため。

注記: Python から Rust への移行のような 10 倍のスループット向上を期待するのは間違いです。主な恩恵は「ばかげたエラーの削減」と「平坦なレイテンシ」にあります。また、チーム間のコード共有機会は増加します(両言語で書けるため)。


結論

Go から Rust への移行は、動的型付けから静的型へ、あるいは遅いランタイムから厳密なコンパイラへの移動ではありません。同じにコンパイルされた言語圏内での、より堅牢なコードベースを志向するものです。

  • 高稼働率とビジネスクリティカル性の高い基盤サービスでは、このトレードオフ(学習曲線の急峻さ)は明確に価値があります。
  • その他の用途やチームの優先順位によっては、Go を継続することが正解です。

移行の目的は、「各問題をそれが最もよく解決する言語」に配置することにあります。後端チームの評価、移行計画の策定、あるいは既存サービスの Rust 化についてご支援が必要な場合は、お問い合わせください。

同じ日のほかのニュース

一覧に戻る →

2026/05/25 3:56

オーストラリアの週 4 日制研究データで生産性が向上したと示唆されました

## 日本語訳: *Nature* の『Humanities and Social Sciences Communications』に発表された新研究によると、4 日勤務週間の試行を継続しているオーストラリア企業のうち 15 社のうち 14 社が「100:80:100 モデル」(完全な給与、80% の労働時間、全出力)を採用していたことが示されています。デイキン大学のジョン・ホプキンス教授を筆頭に、2023 年初頭から 2024 年秋にかけて行われたインタビューでは、不動産管理、出版、ヘルスケア技術、法律、ソフトウェア開発など幅広い業界を対象としました。どの企業でも生産性は低下しておらず、6 つの企業で向上し、9 つの企業が出力を維持していました。1 社は大きな内部変化により試行から退出し、もう 1 つはすでに 8 年間のパイロットプログラムを実施済みでした。バーンアウトが主な要因となったのは 6 社で、これらは単なる収益だけでなく、離職率、欠勤日、病休日、メンタルヘルス休暇をモニタリングしました。これは、2025 年の『Beyond Blue』調査でも示されているように、オーストラリアの労働者の半数がバーンアウトを経験しており、特に若年層と親御さんにおいて顕著であることと整合しています。国際的には、200 社以上の英国企業と 45 社のドイツ企業(主に中小企業)が給与カットなしでこのモデルを採用しています。導入方法は業界のリズムや業種によって異なります:顧客接点を持つ組織は休暇日を分散させる傾向があり一方、医療、緊急サービス、物流、ホスピタリティなどの分野は構造的なスケジュール調整の課題に直面します。企業は不要な会議を削減し、タスクを自動化し、低価値な仕事を排除することでワークフローを合理化しています。批判者は、一部の短期的な利益が新奇効果によるものかもしれないと注意喚起しています。今後を見据え、AI は反復的なタスクを自動化して労働者が時間を取り戻すよう助け、単に日常の業務量を増やすのではなく、将来の成功はウェルビーイングと離職防止を追跡することによって実現されると考えられています。全体としての変化は、効率性と並んで人間のウェルビーイングを最優先とする持続可能なパターンの方向へと向かっています。

2026/05/21 9:15

LAN-LOK:南极向け DOS サボタージュゲーム「34 年間も消失した」作

## Japanese Translation: AlphaPixel は、Mark Chappell および Shane Maloney という研究者により Palmer Station で作成された稀な 1991 年の南极観測ステーション用コンピューターゲーム「LAN-LOK」を成功裏に蘇らせた。本プロジェクトは、同ステーションで初めてのピアツーピア LAN(PalmerLAN/GrapeVine)の設置後に開発が行われた「Evil Al サボタージレース」というタイトルであり、30 年以上も知られていなかったところ、創業者である Chris Hanson が 2025 年に未開封のコピーを発見した。Hanson はその後にゲームの対抗役(悪の AI「Evil Al」)の実在のモデルとなった人物である Al Oxton(「ajo」氏)と連絡を取り、メールを通じて作成の詳細を確認させた。 このプロジェクトは、この廃棄された 16 ビットプログラムを現代的な遊べば良い体験へと変え、現在 Archive.org でアクセスでき、AlphaPixel 経由でダウンロード可能となっている。 gameplay は、プレイヤーがディレクトリを削除したりディスクをフォーマットしたりするなどのサボタージュ行為を行い、AI が制御する「Evil Al」と対戦しながら、特定のターゲット(例:重要な"Hobbs"ノード)への攻撃と勝利に必要なスコア要件を満たすことを目指す 5 分のレースである。長期的な存続性を確保するため、AlphaPixel は Ghidra や Reko といったリバースエンジニアリングツールを用いてレガシーコードのデコンパイルを行い、16 ビットシステムと 64 ビットシステム間の互換性問題を解決するとともに、SDL フレームワークを使用してグラフィックおよび入力処理を更新している。また AI ツールの活用も行う可能性がある。この取り組みは単に南极からユニークなデジタルアーティファクトを救い出しただけでなく、AlphaPixel の広範なレガシーデータの復元に関する専門性を示しており、8 ビットデバイスから現代の RISC-V プロセッサに至るまでのさまざまなアーキテクチャにおいて、エミュレーションされたゲームからフォレンジック動画の回復まで幅広く対応できることを証明している。

2026/05/25 3:39

Jujutsu で Git Rigour Fatigue を克服する

## Japanese Translation: 著者は、コードレビューを「種類別(例:赤で変更内容、青で UI)」に分類し、履歴を確定させる前に視覚的なワークフローを採用するためのステブのジュジュツチュートリアルへの相談を推奨しています。このアプローチは、デバッグによる修正とリファクタリングを単一のブランチ内で混在させるという一般的な誤り(コミットが以前の作業を上書きすることで頻発するコンフリクト)を回避します。標準ツールである `jj absorb`(ファイルの所有者との相性が悪いため課題が多い)や厳格なシーケンシング手法とは異なり、この手法では中間ステップごとにコンパイルしなくてもよい、当初はごちゃまぜの「全コミット」を受け入れることで一時的なデバッグ状態を許容します。ターゲット対象となるクイッシュコマンドを最終段階に留め、特定の変更カテゴリを色分けされた独自のコミットに分離することにより、Git のシーケンシングや複雑な分割の堅牢性を伴わずとも清潔で視覚的な履歴を実現できます。この戦略は、開発中の各個々のコミットがコンパイル可能であるという保証を犠牲にしますが、厳格なステップバイステップのコンパイル要件よりも、明確な視覚的なソートと管理可能なレビュー単位を重視するチームにとって、軽量で柔軟な代替手段を提供します。

Go から Rust へ移行する | そっか~ニュース