Rust プログラミング 入門

Rust完全ガイド 第7回 ファイルとWeb開発

Rustは、高いパフォーマンスと安全性を持ちながら、システムプログラミングだけでなくWeb開発やファイル操作にも活用できる 言語です。
本記事では、Rustを使ったファイルの読み書き、HTTP通信、そして簡単なWebアプリの作成 について学んでいきます。

まず、std::fs を使ったファイルの読み書きと、効率的なバッファリングを行う BufReaderBufWriter の使い方 を解説します。
次に、reqwest を用いたHTTP通信と、serde によるJSONのシリアライズ・デシリアライズ を学び、RustでのAPI通信の基礎を理解します。
最後に、warpactix-web などのWebフレームワークを活用し、簡単なWebサーバーを構築する方法 を実践します。

Rustは、高速で安全な並行処理を活かしながら、Web開発やネットワークプログラミングにも適した言語 です。
この章を通じて、Rustのファイル操作とWeb開発の基本を身につけ、より実用的なプログラムを開発するスキル を習得していきましょう!

ファイル操作

プログラムの実行中にデータを保存したり、外部ファイルから情報を読み取ることは多くのアプリケーションで必要になります。
Rustでは 標準ライブラリの std::fs を活用することで、簡単に ファイルの読み書き を行うことができます。
また、BufReaderBufWriter を利用することで、効率的なファイルの入出力処理 を実現できます。

この章では、std::fs を使った基本的なファイル操作の方法と、バッファリングを活用した最適なファイル処理 について学びます。

std::fsを使ったファイル読み書き

ファイルの読み込み

Rustでは std::fs::read_to_stringstd::fs::File を使用して、簡単にファイルを読み込むことができます。

use std::fs;

fn main() {
    let content = fs::read_to_string("example.txt")
        .expect("ファイルの読み込みに失敗しました");

    println!("ファイルの内容:\n{}", content);
}

1行ずつ読み込む方法

大量のデータを扱う場合、fs::read_to_stringメモリ使用量が大きくなる ため、FileBufReader を使うのが一般的です。

use std::fs::File;
use std::io::{self, BufRead};

fn main() -> io::Result<()> {
    let file = File::open("example.txt")?;
    let reader = io::BufReader::new(file);

    for line in reader.lines() {
        println!("{}", line?);
    }

    Ok(())
}

ファイルの書き込み

Rustでは、fs::write を使うと簡単にデータを書き込むことができます。

use std::fs;

fn main() {
    fs::write("output.txt", "Rustでファイルに書き込み")
        .expect("ファイルの書き込みに失敗しました");

    println!("データを書き込みました");
}

追記モードで書き込む

fs::write ではなく、FileOpenOptions で開くことで 追記(append) することが可能です。

use std::fs::OpenOptions;
use std::io::Write;

fn main() {
    let mut file = OpenOptions::new()
        .append(true)
        .create(true)
        .open("output.txt")
        .expect("ファイルのオープンに失敗しました");

    writeln!(file, "新しい行を追加").expect("書き込みに失敗しました");
}

BufReaderBufWriter

BufReader を使った効率的なファイル読み込み

BufReader は、ファイルを一度にすべて読み込むのではなく、バッファを使って部分的に読み込むことでパフォーマンスを向上させる ために使用されます。

use std::fs::File;
use std::io::{self, BufRead, BufReader};

fn main() -> io::Result<()> {
    let file = File::open("large_file.txt")?;
    let reader = BufReader::new(file);

    for line in reader.lines() {
        println!("{}", line?);
    }

    Ok(())
}

BufWriter を使った効率的なファイル書き込み

BufWriter を使うと、複数回の小さな書き込みをバッファにまとめ、まとめてディスクに書き込むことでパフォーマンスを向上 させることができます。

use std::fs::File;
use std::io::{self, Write, BufWriter};

fn main() -> io::Result<()> {
    let file = File::create("buffered_output.txt")?;
    let mut writer = BufWriter::new(file);

    for i in 1..=5 {
        writeln!(writer, "行 {}", i)?;
    }

    Ok(())
}

まとめ

  • std::fs を使うことで、Rustは簡単にファイルの読み書きを行うことができる。
  • BufReader はメモリ効率よくファイルを読み込むために使われる。
  • BufWriter は頻繁な小さな書き込みをまとめ、パフォーマンスを向上させる。
  • OpenOptions を使うことで、既存のファイルへの追記が可能。

これで Rustのファイル操作の基本 を学びました。

RustでのHTTP通信

WebアプリケーションやAPIクライアントを開発する際、HTTP通信 は不可欠な要素です。
Rustでは reqwest を利用することで、シンプルにHTTPリクエストを送信し、レスポンスを受け取る ことができます。
また、APIのデータフォーマットとして広く使われる JSON の処理には serde を活用し、Rustの構造体と相互変換を行うことで、型安全なデータ処理が可能になります。

この章では、reqwest を使ったHTTPリクエストの基本操作と、serde によるJSONのシリアライズ(変換)・デシリアライズ(解析) について詳しく解説します。

reqwestを使ったHTTPリクエスト

reqwestとは?

reqwest は、RustでHTTP通信を行うためのライブラリであり、
以下のような機能を提供します。

  • GET, POST, PUT, DELETE などのHTTPリクエストの送信
  • 非同期(async/await)対応のリクエスト処理
  • JSONやテキストのレスポンス処理
  • 認証・ヘッダーの追加などの高度なHTTP通信の管理

reqwest のインストール

Cargoプロジェクトで reqwest を利用するには、以下の依存関係を追加します。

[dependencies]
reqwest = { version = "0.11", features = ["json", "blocking"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
  • json を有効化すると、JSONの送受信が可能になる。
  • blocking を有効化すると、同期(blocking)リクエストが利用可能。
  • tokio は非同期処理(async/await)を利用する際に必要。

GETリクエストを送信する

Rustで reqwest を使い、外部APIからデータを取得する方法 を見てみましょう。

use reqwest;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let response = reqwest::get("https://jsonplaceholder.typicode.com/posts/1")
        .await?
        .text()
        .await?;

    println!("レスポンス: {}", response);
    Ok(())
}

コードのポイント

  • reqwest::get("URL") を使うと、GETリクエストを送信できる。
  • await をつけることで、非同期処理として実行される。
  • .text().await? を呼び出すと、レスポンスボディを String として取得できる。

POSTリクエストを送信する

データをAPIに送信する場合、POSTリクエスト を利用します。

use reqwest::Client;
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = Client::new();

    let response = client.post("https://jsonplaceholder.typicode.com/posts")
        .json(&json!({
            "title": "Rust HTTP通信",
            "body": "これはRustからのリクエストです。",
            "userId": 1
        }))
        .send()
        .await?
        .text()
        .await?;

    println!("レスポンス: {}", response);
    Ok(())
}

コードのポイント

  • Client::new() でHTTPクライアントを作成する。
  • post("URL") を呼び出して、POSTリクエストを送信。
  • .json(&json!({...})) を使うと、JSONデータを送信できる。

serdeを使ったJSONのシリアライズ

serde とは?

serde は、Rustのデータ構造(構造体など)とJSONを相互変換するライブラリ です。
API通信では、サーバーから送られてくるJSONデータをRustの型に変換したり、逆にRustのデータをJSONとして送信する場面が多くあります。

serde を活用することで、型安全にJSONの処理を行うことができます。

JSONのデシリアライズ(JSON → Rustの型)

サーバーから受け取ったJSONデータを Rustの構造体に変換する 方法を見てみましょう。

use reqwest;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Post {
    userId: u32,
    id: u32,
    title: String,
    body: String,
}

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let response = reqwest::get("https://jsonplaceholder.typicode.com/posts/1")
        .await?
        .json::()
        .await?;

    println!("受信したデータ: {:?}", response);
    Ok(())
}

コードのポイント

  • #[derive(Deserialize)] を構造体に追加すると、JSONから自動でデータをマッピングできる。
  • response.json::<Post>().await? を使うと、JSONデータを Post 型に変換する。

JSONのシリアライズ(Rustの型 → JSON)

Rustのデータ構造を JSON文字列に変換する 方法を見てみましょう。

use serde::Serialize;
use serde_json;

#[derive(Serialize)]
struct User {
    id: u32,
    name: String,
    email: String,
}

fn main() {
    let user = User {
        id: 1,
        name: "Rustacean".to_string(),
        email: "rust@example.com".to_string(),
    };

    let json_string = serde_json::to_string(&user).unwrap();
    println!("JSONデータ: {}", json_string);
}

コードのポイント

  • #[derive(Serialize)] を構造体に追加すると、データをJSON形式に変換できる。
  • serde_json::to_string(&user).unwrap() を使うと、Rustの型をJSON文字列に変換できる。

まとめ

  • reqwest を使うと、Rustで簡単にHTTPリクエストを送信できる。
  • reqwest::get() でGETリクエスト、client.post() でPOSTリクエストを行える。
  • serde を活用することで、Rustの構造体とJSONを型安全に相互変換できる。
  • #[derive(Deserialize)] でJSONからRustの型へ、#[derive(Serialize)] でRustの型からJSONへ変換可能。

これで Rustを使ったHTTP通信とJSONデータの処理 を学びました。

簡単なWebアプリの作成

Rustはシステムプログラミングだけでなく、Webアプリケーション開発にも適した言語 です。
非同期処理とメモリ安全性を強みとするRustは、高性能で安全なWebサーバーを構築するのに最適な選択肢 となります。

RustにはいくつかのWebフレームワークがありますが、本記事では特に人気の高い warpactix-web を紹介します。
それぞれのフレームワークを利用して、簡単なWebサーバーを構築する方法 を学びます。

warpactix-webを使ったWebサーバー構築

warpとは?

warp は、Rustの非同期ランタイム Tokio 上に構築された、軽量で柔軟なWebフレームワーク です。
以下のような特徴を持っています。

  • 非同期処理に最適化されているasync / await を活用)
  • 型安全なルーティングが可能
  • フィルター(Filter)を活用した直感的なリクエスト処理

warpのインストール

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

[dependencies]
warp = "0.3"
tokio = { version = "1", features = ["full"] }

基本的なWebサーバーの作成

以下のコードは、シンプルなWebサーバーをwarpで構築する例 です。

use warp::Filter;

#[tokio::main]
async fn main() {
    let hello = warp::path!("hello" / String)
        .map(|name| format!("Hello, {}!", name));

    warp::serve(hello)
        .run(([127, 0, 0, 1], 3030))
        .await;
}

コードのポイント

  • warp::path!("hello" / String)/hello/{name} のリクエストを受け取るルートを定義する。
  • map(|name| format!("Hello, {}!", name)) でリクエストパラメータを取得し、レスポンスを返す。
  • warp::serve(hello).run(([127, 0, 0, 1], 3030))localhost:3030 でサーバーを起動する。

このサーバーを実行し、以下のURLにアクセスすると、動作を確認できます。

http://localhost:3030/hello/Rust

レスポンス: Hello, Rust!

actix-webとは?

actix-web は、RustのWebフレームワークの中でも特に高性能でスケーラブルなWebアプリケーションを構築 できるフレームワークです。
以下のような特徴を持っています。

  • マルチスレッド対応で高パフォーマンス
  • 直感的なルーティング機能
  • ミドルウェアによる柔軟な拡張性

actix-webのインストール

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

[dependencies]
actix-web = "4"
tokio = { version = "1", features = ["full"] }

基本的なWebサーバーの作成

以下のコードは、actix-web を使ったシンプルなWebサーバーの例 です。

use actix_web::{web, App, HttpServer, Responder, HttpResponse};

async fn greet(name: web::Path) -> impl Responder {
    HttpResponse::Ok().body(format!("Hello, {}!", name))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/hello/{name}", web::get().to(greet))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

コードのポイント

  • async fn greet()/hello/{name} のエンドポイントを定義する。
  • web::Path<String> を引数に取ることで、URLパラメータを受け取る。
  • HttpServer::new() で新しいWebサーバーを作成し、.bind(("127.0.0.1", 8080)) でポート8080で起動する。

このサーバーを実行し、以下のURLにアクセスすると、動作を確認できます。

http://localhost:8080/hello/Rust

レスポンス: Hello, Rust!

まとめ

  • RustのWebフレームワークには warpactix-web があり、それぞれ異なる特徴を持つ。
  • warp は型安全なルーティングと非同期処理を活用したシンプルなフレームワーク。
  • actix-web はマルチスレッド対応で高性能なWebサーバーを構築できる。
  • どちらのフレームワークも、シンプルなルート定義で簡単にWebサーバーを作成できる。

これで 第7回「ファイルとWeb開発」 の学習が完了しました。
次回の 第8回では「エコシステム活用」 について学びます。
Rustの強力なパッケージマネージャである Cargoの活用方法 や、人気のライブラリ、テスト・デバッグの手法を詳しく解説し、より実用的な開発環境を整えていきます!

  • この記事を書いた人

ふくまる

機械設計業をしていたが25歳でエンジニアになると決意して行動開始→ 26歳でエンジニアに転職→ 28歳でフリーランスエンジニアに→ 現在、34歳でフリーランス7年目 Go案件を受注中 Go,GCPが得意分野

-Rust, プログラミング, 入門