GEMINI LABEN
NANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定ですNANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定です
記事一覧/API / SDK
API / SDK/2026-04-06初級

Gemini API を Rust で使う入門 — テキスト生成・ストリーミング・マルチモーダルの実装

Rust から Gemini API を呼び出す実践ガイド。reqwest クレートを使ったテキスト生成・ストリーミング・マルチモーダル入力の実装方法を、コード例とともにわかりやすく解説します。

gemini-api279rust2テキスト生成ストリーミング11reqwest入門4

Gemini API の公式 SDK 一覧を眺めると、Python・JavaScript・Go・Java・Kotlin・Swift・Dart と並んでいて、Rust だけがありません。ゼロコスト抽象化とメモリ安全性を求めてバックエンドを Rust で組んでいる方には、少し寂しい並びです。

ただ、これは実質的な障害になりません。Gemini API は素直な RESTful エンドポイントを公開しているので、HTTP クライアントクレートの reqwest があれば数十行で繋がります。扱うのは次の5点です。

  • Cargo プロジェクトのセットアップと必要なクレートの追加
  • Gemini API へのテキスト生成リクエスト(非同期)
  • Server-Sent Events(SSE)を使ったストリーミングレスポンスの受信
  • Base64 エンコードした画像を送るマルチモーダル入力
  • よくあるエラーのトラブルシューティング

対象読者は Rust の基礎文法を理解している方 で、AI API の利用は初めてでも問題ありません。


前提条件・環境準備

必要なもの

  • Rust(1.75 以降推奨): rustup update stable
  • Gemini API キー: Google AI Studio で無料取得可能
  • 安定したインターネット接続

API キーの取得

Google AI Studio にアクセスし、Google アカウントでサインイン後、「Get API key」から新しいキーを作成してください。取得したキーは環境変数として管理します。

export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"

セキュリティ注意: APIキーをソースコードに直接書き込まないでください。dotenv クレートや環境変数を活用しましょう。

Cargo プロジェクトの作成

cargo new gemini-rust-demo
cd gemini-rust-demo

Cargo.toml に以下の依存関係を追加します。

[dependencies]
reqwest = { version = "0.12", features = ["json", "stream"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
dotenvy = "0.15"
base64 = "0.22"
futures-util = "0.3"

テキスト生成 — 基本的な API 呼び出し

まずは最もシンプルなテキスト生成から始めましょう。Gemini API の /generateContent エンドポイントに JSON ペイロードを送信します。

use reqwest::Client;
use serde_json::{json, Value};
use std::env;
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 環境変数から API キーを取得
    let api_key = env::var("GEMINI_API_KEY")
        .expect("GEMINI_API_KEY が設定されていません");
 
    let client = Client::new();
 
    // リクエストボディを構築
    let body = json!({
        "contents": [{
            "parts": [{
                "text": "Rust プログラミング言語の特徴を3つ挙げて、それぞれ簡潔に説明してください。"
            }]
        }]
    });
 
    // Gemini 2.5 Flash にリクエストを送信
    let url = format!(
        "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={}",
        api_key
    );
 
    let response = client
        .post(&url)
        .header("Content-Type", "application/json")
        .json(&body)
        .send()
        .await?;
 
    let json: Value = response.json().await?;
 
    // レスポンスからテキストを取り出す
    let text = &json["candidates"][0]["content"]["parts"][0]["text"];
    println!("Gemini の回答:\n{}", text.as_str().unwrap_or("回答を取得できませんでした"));
 
    Ok(())
}

期待する出力例:

Gemini の回答:
Rust プログラミング言語の3つの主な特徴:

1. **メモリ安全性**: ガベージコレクタなしにメモリ安全性を保証します。...
2. **高いパフォーマンス**: C/C++ に匹敵する実行速度を実現します。...
3. **ゼロコスト抽象化**: 高レベルの抽象化が実行時コストを生みません。...

システムインストラクションの追加

Gemini API では System Instructions を設定することで、モデルの役割や応答スタイルを定義できます。

let body = json!({
    "system_instruction": {
        "parts": [{
            "text": "あなたは Rust の専門家です。技術的な正確さを保ちながら、初心者にもわかりやすく説明してください。"
        }]
    },
    "contents": [{
        "role": "user",
        "parts": [{
            "text": "所有権(Ownership)とは何ですか?"
        }]
    }]
});

ストリーミングレスポンスの実装

長い応答を待たずにリアルタイムで受信するには、streamGenerateContent エンドポイントを使います。Rust では futures-util を使って SSE ストリームを処理できます。

use futures_util::StreamExt;
use reqwest::Client;
use serde_json::{json, Value};
use std::env;
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = env::var("GEMINI_API_KEY")?;
    let client = Client::new();
 
    let body = json!({
        "contents": [{
            "parts": [{
                "text": "Rust の非同期プログラミングについて詳しく説明してください。"
            }]
        }]
    });
 
    let url = format!(
        "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?key={}&alt=sse",
        api_key
    );
 
    let mut stream = client
        .post(&url)
        .header("Content-Type", "application/json")
        .json(&body)
        .send()
        .await?
        .bytes_stream();
 
    // ストリームをチャンクごとに処理
    while let Some(chunk) = stream.next().await {
        let bytes = chunk?;
        let text = String::from_utf8_lossy(&bytes);
 
        // "data: " プレフィックスを除去して JSON を解析
        for line in text.lines() {
            if let Some(json_str) = line.strip_prefix("data: ") {
                if json_str.trim() == "[DONE]" {
                    break;
                }
                if let Ok(json) = serde_json::from_str::<Value>(json_str) {
                    if let Some(part_text) = json["candidates"][0]["content"]["parts"][0]["text"].as_str() {
                        print!("{}", part_text); // リアルタイム出力
                        use std::io::Write;
                        std::io::stdout().flush()?;
                    }
                }
            }
        }
    }
    println!(); // 改行
    Ok(())
}

マルチモーダル入力 — 画像の解析

Gemini のマルチモーダル能力を活かして、画像をテキストと組み合わせて送信できます。画像ファイルを Base64 エンコードして inlineData として渡します。

use base64::{engine::general_purpose, Engine as _};
use reqwest::Client;
use serde_json::{json, Value};
use std::{env, fs};
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = env::var("GEMINI_API_KEY")?;
    let client = Client::new();
 
    // 画像ファイルを読み込んで Base64 エンコード
    let image_bytes = fs::read("sample.jpg")?;
    let base64_image = general_purpose::STANDARD.encode(&image_bytes);
 
    let body = json!({
        "contents": [{
            "parts": [
                {
                    "inline_data": {
                        "mime_type": "image/jpeg",
                        "data": base64_image
                    }
                },
                {
                    "text": "この画像に写っているものを詳しく説明してください。"
                }
            ]
        }]
    });
 
    let url = format!(
        "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={}",
        api_key
    );
 
    let response = client.post(&url).json(&body).send().await?;
    let json: Value = response.json().await?;
 
    println!(
        "{}",
        json["candidates"][0]["content"]["parts"][0]["text"]
            .as_str()
            .unwrap_or("解析結果を取得できませんでした")
    );
 
    Ok(())
}

マルチターン対話(チャット)の実装

会話の文脈を保持するには、過去の contents 配列を累積して送り続けます。

use reqwest::Client;
use serde_json::{json, Value};
use std::env;
use std::io::{self, BufRead, Write};
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = env::var("GEMINI_API_KEY")?;
    let client = Client::new();
    let url = format!(
        "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={}",
        api_key
    );
 
    // 会話履歴を Vec で管理
    let mut conversation: Vec<Value> = Vec::new();
    let stdin = io::stdin();
 
    println!("Gemini とチャットを開始します('quit' で終了)");
 
    for line in stdin.lock().lines() {
        let user_input = line?;
        if user_input.trim() == "quit" {
            break;
        }
 
        // ユーザーメッセージを履歴に追加
        conversation.push(json!({
            "role": "user",
            "parts": [{ "text": user_input }]
        }));
 
        let body = json!({ "contents": conversation });
        let response = client.post(&url).json(&body).send().await?;
        let json: Value = response.json().await?;
 
        if let Some(reply) = json["candidates"][0]["content"]["parts"][0]["text"].as_str() {
            println!("Gemini: {}", reply);
 
            // アシスタントの返答を履歴に追加
            conversation.push(json!({
                "role": "model",
                "parts": [{ "text": reply }]
            }));
        }
 
        print!("You: ");
        io::stdout().flush()?;
    }
 
    Ok(())
}

よくあるエラーと対処法

Rust で Gemini API を使う際に遭遇しやすいエラーをまとめます。

400 Bad Request

JSON の構造が正しくない場合に発生します。serde_json::to_string_pretty(&body) でリクエストボディを確認しましょう。

403 API_KEY_INVALID

API キーが無効または環境変数が設定されていない場合です。echo $GEMINI_API_KEY で確認してください。

429 RESOURCE_EXHAUSTED

レート制限に達しています。無料ティアでは 1 分あたりのリクエスト数に上限があります。tokio::time::sleep でリトライ間隔を設けましょう。

use tokio::time::{sleep, Duration};
 
// 指数バックオフによるリトライ
for attempt in 0..3 {
    let response = client.post(&url).json(&body).send().await?;
    if response.status() == 429 {
        let wait_secs = 2_u64.pow(attempt);
        eprintln!("レート制限。{}秒後にリトライします...", wait_secs);
        sleep(Duration::from_secs(wait_secs)).await;
        continue;
    }
    // 成功処理...
    break;
}

reqwest の TLS エラー

Cargo.tomlreqwestrustls-tls フィーチャーが必要な環境もあります。

reqwest = { version = "0.12", features = ["json", "stream", "rustls-tls"] }

さらに詳しい実装テクニック(Function Calling・ツール統合・プロダクション設計)については、Gemini API Function Calling完全ガイド で解説しています。


まとめ

ここではRust から Gemini API を呼び出す方法を基礎から解説しました。

  • reqwest + serde_json の組み合わせで REST API を簡単に呼び出せる
  • streamGenerateContent エンドポイントと SSE でリアルタイムなストリーミングを実現できる
  • inline_data を使えばマルチモーダル(画像解析)も対応可能
  • 会話履歴を Vec で管理することでマルチターン対話も実装できる

Rust の安全性とパフォーマンスを活かしながら Gemini の AI 能力を組み合わせることで、堅牢な AI バックエンドサービスを構築できます。次のステップとして、Axum や Actix-web などの Web フレームワークと組み合わせた REST API サーバーの構築に挑戦してみてください。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-05-15
Expo + Gemini APIでAIチャットアプリを個人開発して審査まで通した実装記録
Expo と Gemini API で AI チャットアプリを作り、両ストアの審査を通すまでの実装記録です。ストリーミングの実測レイテンシ、画像入力の落とし穴、コスト上限の設計、リリース前チェックリストまでまとめました。
API / SDK2026-04-18
Gemini API の無料枠で最初のプロジェクトを作る — 課金ゼロで始めるAI開発入門
Gemini API の無料枠(Free Tier)の実際の上限と制約を正直に解説。課金ゼロでどこまで作れるか、入門プロジェクト3つのサンプルコード付きで紹介します。
API / SDK2026-07-18
同じ gemini-flash-latest が、リージョンごとに違うモデルを指していた — 比較値が汚れる数日間の見つけ方
エイリアスの解決先はリージョン単位でロールアウトされます。同じ gemini-flash-latest を東京と us-central1 に投げると、数日だけ別のモデルが応答する。その間の比較値が無効になる理由と、1発のプローブで検知して比較を凍結する設計をまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →