Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OCHa Cafe S10 #3 Rustを学ぼう!

OCHa Cafe S10 #3 Rustを学ぼう!

デモやサンプルで用いたソースコードは以下にあります。
https://github.com/oracle-japan/ochacafe-rust

Avatar for oracle4engineer

oracle4engineer PRO

August 20, 2025
Tweet

More Decks by oracle4engineer

Other Decks in Technology

Transcript

  1. アジェンダ 2 Copyright © 2025, Oracle and/or its affiliates 1

    2 3 4 5 6 はじめに Rustとは何か? Rustの特性と書き方 Rustのエコシステムとフレームワーク Rustを書いてみよう まとめ
  2. 自己紹介 Kyotaro Nonaka 野中恭大郎 Solutions Architect • Java EE/Messaging/NoSQL/Frontend •

    Oracle Cloud Hangout Café メンバー • Oracle Groundbreakers Advocate • 元ERPパッケージ開発者 • 趣味は釣りとかキャンプとか • 最近は好きなバンドの来日公演が多くて嬉しいです。 - KASABIAN @ Zepp Haneda - 済 - MUSE @ Kアリーナ - OASIS @ 東京ドーム – New! - Franz Ferdinand @ 東京ガーデンシアター 3 Copyright © 2025, Oracle and/or its affiliates @non_kyon
  3. Rustってどんな言語? “A language empowering everyone to build reliable and efficient

    software.” — rust-lang.org パフォーマンス • 高速でメモリ効率が高い • ランタイムやガベージコレクタがない • 組込み機器上の実行も可能 信頼性 • メモリ安全性とスレッド安全性が保証 - 豊かな型システム - 所有権モデル • 様々な種類のバグをコンパイル時に排除 生産性 • 有用なエラーメッセージを備えた使いやすいコンパイラ • 統合されたパッケージマネージャとビルドツール • 多数のエディタに対応する自動補完と型検査機能 • 自動フォーマッタ 6 Copyright © 2025, Oracle and/or its affiliates
  4. Rustの特徴 7 Copyright © 2025, Oracle and/or its affiliates メモリ安全

    • 所有権 • 借用 • バグをコンパ イル時に防止 高速実行 • C/C++に匹 敵するネイ ティブパフォー マンス モダンな構文 • 型推論 • パターンマッチ • ジェネリクス 安全な並行性 • データ競合を 防ぐ設計 パッケージ管理 • cargoでビル ド・依存・実 行を一括管 理
  5. なぜRustは「安全」で「高速」なのか? • コンパイル時にメモリ安全を保証 - 解放忘れ・二重解放・ポインタバグを防ぐ - 「実行してみないと分からない」ではなく「そもそもビルドが通らない」 • ガーベジコレクション不要 -

    実行時オーバーヘッドがない - 代わりに「所有権」でライフタイムを管理 • データ競合のない並行処理 (Fearless Concurrency) - 複数スレッドを扱うときのデータ競合をコンパイル時に防ぐ • 高レベル抽象でもオーバーヘッドなし (ゼロコスト抽象化) - human readableな記述でもコンパイル時に最適化 8 Copyright © 2025, Oracle and/or its affiliates
  6. Cloud NativeとRustの相性の良さ • 小さくて高速なCLIツール開発に最適 • Web Assembly (WASM) 対応が強力 •

    KubernetesエコシステムにRust製ツールが増加中 - krustlet • KubernetesのノードとしてWASMを動かすためのkubelet実装 • Rustで書かれていて、安全かつ軽量なWASM実行環境を提供 - kube-rs • Kubernetes APIと連携するRustクレート(= ライブラリ) • Rustでk8sのControllerやOperatorを簡単に構築 - wasmtime • WASI準拠のWebAssemblyランタイム • Cloud Nativeなサーバーレス・サンドボックス環境で注目 9 Copyright © 2025, Oracle and/or its affiliates いつかWASM はOCHaCafe でやりたい
  7. RustとWebAssembly (WASM) WASM • ブラウザやランタイムで動作するバイナリフォーマット • 高速・安全・移植性が高い - 移植性 :

    WASMバイナリが環境やOSに縛られず動作する性質 RustがWASMに向いている理由 • メモリ安全をコンパイラが保証 → 安全にWASMへ変換可能 • C/C++の代替としての利用(低レベルの制御と高レベルの表現を両立) • エコシステムが充実 - wasm-pack : Rust → WASM コンパイラ - wasmtime : Rust製 WASM ランタイム 利用シーン • フロントエンド : ブラウザでの高速処理 • サーバーサイド : WASI (WebAssembly System Interface) を通じた安全な拡張 • Cloud Native : サービスメッシュの拡張/カスタマイズ (Envoy の WASM SDK も Rust をサポート) 10 Copyright © 2025, Oracle and/or its affiliates Rustは「安全・高速」をそのままWASMに持ち込める
  8. Rustは他言語と何が違う? 11 Copyright © 2025, Oracle and/or its affiliates 項目

    Rust Go C++ Java TypeScript 実行速度 非常に速い 速い 非常に速い 中〜速 中 (JS依存) メモリ安全性 高 (コンパイル時) 中 (ランタイム) 低 (手動管理) 中〜高 (GC) 中〜高 (JSエンジン) 学習コスト 高め 低 高 中 低 並行性 高 (安全) 中 (goroutine) 高 (unsafe) 中 (スレッドベース) 低 (Web依存) GCの有無 なし あり なし あり あり 主な用途例 CLI・WASM・k8s Web/APIツール 組込み・ゲーム Web/業務アプリ フロントエンド
  9. 変数を定義 • let で変数宣言 • mut をつけると値を変更できる • デフォルトはimmutable 13

    Copyright © 2025, Oracle and/or its affiliates fn main() { let x = 5; // 不変(immutable)変数 let mut y = 10; // 可変(mutable)変数 println!("x = {}, y = {}", x, y); }
  10. 所有権 • Rustの変数には「所有権」が結びついている • 所有権ルールのおかげでメモリ管理は自動 • 定義した変数がスコープを抜けると、自動的にリソースも解放 14 Copyright ©

    2025, Oracle and/or its affiliates 関数ブロック { let a = String::from(“hi”); // 今ココ let b = a; } { 別の処理 } ヒープ領域 “hi” aが”hi”の所有権を持つ a hi
  11. 所有権 • Rustの変数には「所有権」が結びついている • 所有権ルールのおかげでメモリ管理は自動 • 定義した変数がスコープを抜けると、自動的にリソースも解放 15 Copyright ©

    2025, Oracle and/or its affiliates 関数ブロック { let a = String::from(“hi”); let b = a; // 今ココ } { 別の処理 } ヒープ領域 “hi” bに”hi”の所有権が移動 b hi aは使えなくなる = Rust では、値を持たない空の 変数は存在しない a
  12. 所有権 • Rustの変数には「所有権」が結びついている • 所有権ルールのおかげでメモリ管理は自動 • 定義した変数がスコープを抜けると、自動的にリソースも解放 16 Copyright ©

    2025, Oracle and/or its affiliates 関数ブロック { let a = String::from(“hi”); let b = a; } { 別の処理 //今ココ } ヒープ領域 ヒープ領域が解放され、 bが破棄される b
  13. 所有権 : 値には持ち主がいる • 1つの変数だけが値の持ち主になれる • 3行目で所有権はs2に「移動」している • s1の所有権がs2に移ったので、もうs1は使えない 17

    Copyright © 2025, Oracle and/or its affiliates fn main() { let s1 = String::from("hello"); let s2 = s1; // 所有権がs1からs2へ移動 println!("{}", s1); // コンパイルエラー }
  14. 所有権 : 所有権のコピー • clone()を使うと所有権の移動ではなく「コピー」が 発生する • ヒープのメモリコピーが行われるためコストが高い 18 Copyright

    © 2025, Oracle and/or its affiliates fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); // ディープコピー println!("{}", s1); // OK! }
  15. 借用 : 一時的に貸し出す仕組み • & をprefixにすると参照渡し - 「借用」と呼ぶ - 「借用」された値はあくまでも参照なので基本

    的にはimmutable • 借用中でも持ち主が保持されているから、安全 19 Copyright © 2025, Oracle and/or its affiliates fn print_len(s: &String) { println!("len = {}", s.len()); } fn main() { let s = String::from("hello"); print_len(&s); // 借用(参照渡し) println!("{}", s); // OK }
  16. 借用 : 可変参照 • &mut : 可変参照 - 値そのものを借りるが、 中身を変更してもいいとい

    う許可を得るための構文 • Rustは同時に複数の可変参照を禁止してデータ 競合を防ぐ • コンパイル時にチェックしてくれるのがRustの強み 20 Copyright © 2025, Oracle and/or its affiliates fn main() { let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; // コンパイルエラー }
  17. 型を定義 • Rustの型注釈は「変数名: 型」の順番 • 明示しなくても推論してくれるが、書けばもち ろん可読性の向上に寄与 21 Copyright ©

    2025, Oracle and/or its affiliates let a: i32 = 42; let b: f64 = 3.14; let c: &str = "hello"; // 文字列リテラルの参照
  18. 型の一例 22 Copyright © 2025, Oracle and/or its affiliates 型

    説明 例 i32 32ビット整数 let x: i32 = 5; f64 64ビット浮動小数点 let pi: f64 = 3.14; bool 真偽値 let b: bool = true; &str 文字列スライス (参照) "hello" String ヒープ上の文字列 String::from("hi")
  19. String と &str の違い 23 Copyright © 2025, Oracle and/or

    its affiliates • &str : 文字列スライス、値の所有権を持たない • String : ヒープ上の文字列、値の所有権を持つ • “hi” は &str (参照) であり、Stringとは別物 • String::from(...) を使うことでヒープ上に確保された所有権のある文字列を作る • s1は静的にプログラムに埋め込まれた読み取り専用の文字列 • s2はヒープ上に確保され、あとで変更や所有権の移動ができる let s: String = String::from("hi"); let s1 = “hi”; // &str型 (静的文字列) let s2 = String::from(“hi”); // String型 (所有権あり)
  20. 関数を定義 • 戻り値の型は -> で表現 • Rustでは関数の最後の式が返される - セミコロン (;)

    がある = 値が返らない - セミコロン (;) がない = 値が返る • returnを使ってどの位置でも値を返せる - 複雑な条件がある場合はreturnを使うと 明確になる - セミコロンを忘れずに! 24 Copyright © 2025, Oracle and/or its affiliates fn add(a: i32, b: i32) -> i32 { a + b // 最後の式が返る } fn square(x: i32) -> i32 { return x * x; } fn maybe_negative(x: i32) -> i32 { if x < 0 { return 0; } x // 式として返る(正のとき) }
  21. 代表的な制御構文 if / else if / else • 条件は必ず bool

    型 • 三項演算子なし • 式として値を返せる loop • 無限ループ構文 • break で脱出、continue で次のループへ • break 値; とすればループから値を返せる 25 Copyright © 2025, Oracle and/or its affiliates if x > 0 { println!("正"); } else if x < 0 { println!("負"); } else { println!("ゼロ"); } let mut count = 0; loop { count += 1; if count == 3 { break; } }
  22. 代表的な制御構文 while • 条件が false になるまで繰り返す for in • 範囲(Range)やイテレータを使って反復

    • 0..3 は 0, 1, 2(終端は含まない) 26 Copyright © 2025, Oracle and/or its affiliates let mut n = 0; while n < 3 { println!("{}", n); n += 1; } for num in 0..3 { println!("{}", num); }
  23. Rustのエラーハンドリング哲学 try/catch の概念が存在しない • エラーか成功かを値で返す • 「プログラムが途中で落ちる」より、「明示的に処理する」 ことを優先 • 結果的に、見落としによるエラーをコンパイル時に防げる

    Rustの型による保証成功 • パスと失敗パスの両方を型として表現 - Result<T, E> → 成功(T)か失敗(E)のどちらか - Option<T> → 値がある(Some)か、ない(None)か • 復旧できないエラーは panic! → プロセスが即終了 • 型システムにより「必ずどちらかを処理する」ことをコンパイ ル時に強制 27 Copyright © 2025, Oracle and/or its affiliates 言語 エラーの扱い 見落としやすさ コンパイル時 検出 Java 例外 (throw/catch) 発生箇所が隠 れやすい チェック例外の み一部可能 Go error型を戻り値に 加える 無視しようと思 えばできる 型上は強制さ れない Rust Result/Option型 無視不可(未 処理だとコンパ イルエラー) 100%型で 保証
  24. Result型の基本 • Ok() → 成功したときの値 • Err() → エラーの内容 •

    関数の戻り値でエラーを表現 28 Copyright © 2025, Oracle and/or its affiliates fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("ゼロ除算です")) } else { Ok(a / b) } }
  25. matchで結果を処理 • match式のResult(ここでは戻り値の String)がreturnのように関数から返 却 • 全てのパターンでResultの型は同じ型 (ここではString)でなければならない 29 Copyright

    © 2025, Oracle and/or its affiliates fn check_number(x: i32) -> String { match x { 0 => String::from("ゼロです"), 1..=9 => String::from("1から9の間です"), _ => String::from("その他です"), } }
  26. Option型の利用 • Some(value) → 値あり • None → 値なし(Null safety)

    • match で網羅的に扱う • 「ある場合」と「ない場合」を強制的に コードに書かせる 30 Copyright © 2025, Oracle and/or its affiliates fn find_user(id: i32) -> Option<String> { if id == 1 { Some(String::from("Alice")) } else { None } } fn main() { match find_user(1) { Some(name) => println!("Hello, {}", name), None => println!("User not found"), } }
  27. unwrapの利用とpanic! • Some → 値を返す • None → panic! で即終了

    - 簡単だが、安全性は下がる panic! の位置づけ • ユーザー入力や外部要因エラーでは 使わない • プログラムのバグや復旧不能時のみ • Goのpanic()に近いが、Rustでは日 常的には避ける 31 Copyright © 2025, Oracle and/or its affiliates let s = Some("Hello").unwrap(); // "Hello" が返る let x: Option<i32> = None; let n = x.unwrap(); // panic! 発生
  28. ? 演算子の利用 ? は 「エラーや None を見つけたら即 座にその関数から返す」 という動作 •

    Some → 値を取り出す • None → 関数から即返却 • Result 型でも同じ書き方が可能 ※usize: 主にメモリ上のインデックス やサイズを表すために使われる整数 型の一つ 32 Copyright © 2025, Oracle and/or its affiliates fn get_length(name: Option<&str>) -> Option<usize> { let s = name?; // Noneなら即return None Some(s.len()) }
  29. エラーハンドリングまとめ 機能 目的 主な使い方 エラー時の動作 メリット 注意点 Result<T, E> 成功/失敗の両

    方を型で表す Ok(value), Err(error) 失敗時は Err(error)を返 す エラー型を明示で き、安全 呼び出し元が必 ず処理する必要 Option<T> 値の有無を型で 表す Some(value), None 無い場合は None Null Safetyで 値がない場合を 安全に表現 None時の分岐 処理が必要 match 値の全パターンを 網羅的に処理 match x { ... } 各パターンのコー ドが実行される コンパイル時に網 羅性チェック 冗長になりがち unwrap() 値を直接取り出 す(成功前提) x.unwrap() None / Err → panic! コードが短くなる エラー時に即終 了、例外的に利 用 ? 演算子 エラー/None時 に即返す x? None または Err を呼び出し 元に返す 簡潔で安全、 panicしない 関数の戻り値が Result/Option である必要性 33 Copyright © 2025, Oracle and/or its affiliates
  30. Rustのエコシステム全体像 Rustの主要な開発要素 • ビルド & 依存管理 → cargo • パッケージ管理

    → crates.io - Rustではパッケージ = クレート • 豊富なクレート群(公式/OSS) • 実行環境・フレームワーク - CLI - Web - 非同期 - Cloud Native - WASM 35 Copyright © 2025, Oracle and/or its affiliates Rust開発はcargoを中心に回る Implement Build Test crates.io
  31. cargo (パッケージマネージャ & ビルドツール) プロジェクトの作成から実行まで統合管理 $ curl https://sh.rustup.rs -sSf |

    sh 36 Copyright © 2025, Oracle and/or its affiliates ビルド・依存管理・テスト $ cargo new myapp # 新規プロジェクト作成 $ cargo build # ビルド $ cargo run # 実行 $ cargo test # テスト $ cargo doc --open # ドキュメント生成 myapp/ ├── Cargo.toml └── src/ └── main.rs
  32. Cargo.toml の役割 • Rust プロジェクトの設定ファイル • プロジェクトのメタデータ (名前・バージョン・作者)と依存クレートを管理 • TOML形式で記述

    $ cargo new myapp すると… このようなCargo.tomlが出来上がる 依存の追加は [dependencies] セクションにクレート名とバージョンを書くか、cargo add コマンドを使う 37 Copyright © 2025, Oracle and/or its affiliates [package] name = "myapp" version = "0.1.0" edition = "2024" [dependencies]
  33. 依存追加の例 Cargo.toml に記述 コマンドで追加する場合 (cargo-edit のインストールが必要) Cargo.lock ファイル • 初回依存解決時に

    Cargo.lock が作成され、バージョンが固定される • 依存のバージョン差異による不具合を防ぐ • ライブラリ開発ではCargo.lockは共有しないのが一般的 38 Copyright © 2025, Oracle and/or its affiliates [dependencies] serde = "1.0" reqwest = { version = "0.11", features = ["json"] } $ cargo add serde $ cargo add reqwest --features json Note : バージョン指定のルール • "1.0" → セマンティックバージョニングで互換範囲 (1.0.xまで) • "=1.0.3" → 完全一致 • ">=1.0, <2.0" → 範囲指定 • { git = "https://github.com/org/repo" } → Gitから取得 • { path = "../local_crate" } → ローカルディレクトリ から取得
  34. crates.io - 公式パッケージレジストリ crates.io : Rustのnpm的存在 • Rust公式のパッケージ(クレート)配布サイト • https://crates.io

    • 無料で公開クレートを検索・ダウンロード可能 • 利用はCargoと完全統合されており、Cargo.tomlに記述すれ ば自動取得 • クレート検索 - 名前、キーワード、カテゴリ、ダウンロード数で絞り込み • 人気ランキング - 利用実績や更新頻度の高いクレートを確認 • 詳細ページ - バージョン履歴、依存関係、README、ドキュメントリンク 39 Copyright © 2025, Oracle and/or its affiliates
  35. よく使うクレート Rustはネイティブバイナリを生成するため、配布が簡単で高速なCLIを作りやすい 代表的なクレートを組み合わせることで、実用的なツールを短時間で構築できる • clap / structopt : コマンドライン引数解析 (structoptはclapのラッパー)

    • anyhow : エラーを簡単に扱えるAPI • thiserror : カスタムエラー型の定義を簡略化 • indicatif : プログレスバー・進行状況表示 40 Copyright © 2025, Oracle and/or its affiliates CLIツールを例にしてみる
  36. 非同期処理 / Webフレームワーク 非同期ランタイム tokio : Rust非同期処理のデファクト、豊富なエコシステム async-std : Node.js風API、学習コスト低め

    ▪asyncを付けて非同期処理 ※ async はRustの組み込み構文なので、コンパイル自体は tokioなどのランタイムは必要ないが、実際に動かすにはランタイ ムが必要 Webフレームワーク axum : tokioベース、シンプル&型安全、モジュール化しやすい actix-web : 高性能、商用サービス採用例多数 warp : フィルターチェーン型API、柔軟なルーティング ▪axumで簡単なHTTPサーバー 41 Copyright © 2025, Oracle and/or its affiliates Rustはスレッド+非同期I/Oで効率的にリソースを利用可能 async fn fetch_data() -> String { "Hello".to_string() } use axum::{Router, routing::get}; #[tokio::main] async fn main() { let app = Router::new() .route("/", get(|| async { "Hello, Rust!" })); axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); }
  37. #[tokio::main] ← これ何? • コード生成のための仕組み • 実行時ではなく、コンパイル時に展開される • 関数と異なり、型や構造を動的に変えることも可能 宣言的マクロ

    (macro_rules!) • パターンマッチでコードを展開する • println!, vec! 手続き的マクロ • ソースコードを解析して新しいコードを生成 • #[derive(Debug)], #[tokio::main] - #[tokio::main] は非同期関数を普通のmain関数として動かせるように変換するマクロ - 裏でランタイムの初期化コードを自動生成している 42 Copyright © 2025, Oracle and/or its affiliates マクロ : コンパイル前にコードを生成する仕組み
  38. Cloud Native と Rust Cloud Native的な目線でRustが活きる特長 • メモリ安全 + 高パフォーマンス

    - C/C++並みの速度 - ガーベジコレクションなしで低レイテンシ • 単一バイナリ配布 - コンテナイメージを小さくできる (数MB〜) - 依存関係の管理が容易 • クロスコンパイルが容易 - ARM / x86 / WASMなど幅広いターゲットに対応 ▪主な関連分野とクレート 43 Copyright © 2025, Oracle and/or its affiliates 分野 クレート/プロジェクト 概要 Kubernetes操作 kube-rs Kubernetes APIとやり取 りするRustクライアント WASM実行 wasmtime / wasmer WebAssemblyランタイム バインディング bindgen C/C++との相互運用コード を自動生成 ネットワーク hyper, reqwest 高速HTTPクライアント/サー バー gRPC tonic gRPC実装(Protocol Buffers対応)
  39. Hello Rust ↓出来上がるファイル 45 Copyright © 2025, Oracle and/or its

    affiliates • fn main() : エントリーポイント • println! : マクロ、文字列を標準出力へ表示する • cargo run : コンパイルして実行まで fn main() { println!("Hello, world!"); } $ cargo new myapp $ cd myapp $ cargo run Compiling myapp v0.1.0 (/home/opc/develop/rust/myapp) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s Running `target/debug/myapp` Hello, world!
  40. cargo コマンド $ cargo check # 速い構文/型チェック $ cargo build

    # デバッグビルド $ cargo build --release # 本番向け最適化 $ cargo run # ビルドして実行 $ cargo test # テスト実行 $ cargo doc --open # ドキュメント生成 46 Copyright © 2025, Oracle and/or its affiliates
  41. Result / ? を体験してみる • ? は panic! せずに上位に返す •

    main が Result を返すと、? が自然に使える • 「エラー処理」を明示的に型で扱うのがRust 47 Copyright © 2025, Oracle and/or its affiliates use std::fs; fn main() -> Result<(), std::io::Error> { let text = fs::read_to_string(“input.txt”)?; // ファイルがないとreturn Err println!("{}", text); Ok(()) }
  42. clap でCLIを作る 依存関係を追加してみる main.rs を書き換える 48 Copyright © 2025, Oracle

    and/or its affiliates clap : CLI開発用クレート、コマンドライン引数解析を行う [dependencies] clap = { version = "4", features = ["derive"] } use clap::Parser; use std::fs; #[derive(Parser, Debug)] struct Args { path: String, // ファイルパス } fn main() -> Result<(), std::io::Error> { let args = Args::parse(); let text = fs::read_to_string(&args.path)?; println!("読込ファイル: {}", args.path); println!("{}", text); Ok(()) } $ cargo run -- files/input.txt Compiling myapp v0.1.0 (/home/opc/develop/rust/myapp) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.44s Running `target/debug/myapp files/input.txt` 読み込み対象: files/input.txt Hello OCHaCafe S10 Rust 実行
  43. ソースコード解説 • Cargo.toml の features - クレートごとに機能が定義されている - features =

    ["derive"] は「derive マクロ」を有効化 • #[derive(Parser)] を付けられる - 依存関係は必要な機能だけ読み込むのが基本思想 • 軽量性やコンパイル速度に寄与。 • use - モジュールやライブラリをスコープにimportするキーワード • struct - Go の struct、Java のClassに近い - Classのようにメソッドや継承は持たず、データの塊を定義 - derive(Parser) を付けると、この構造体が コマンドライン引数の 仕様になる • :: (パス演算子) - モジュールや型の中にある要素にアクセスする記号 - Javaでいう . (ドット) 49 Copyright © 2025, Oracle and/or its affiliates [dependencies] clap = { version = "4", features = ["derive"] } use clap::Parser; use std::fs; #[derive(Parser, Debug)] struct Args { path: String, // ファイルパス } fn main() -> Result<(), std::io::Error> { let args = Args::parse(); let text = fs::read_to_string(&args.path)?; println!("読込ファイル: {}", args.path); println!("{}", text); Ok(()) }
  44. CLIツール実装チートシート before after 50 Copyright © 2025, Oracle and/or its

    affiliates 1. --help を Readable に use clap::Parser; use std::fs; #[derive(Parser, Debug)] #[command(name="myapp", version, about="テキストを 表示するCLI")] struct Args { path: String, // ファイルパス } fn main() -> Result<(), std::io::Error> { let args = Args::parse(); let text = fs::read_to_string(&args.path)?; println!("読込ファイル: {}", args.path); println!("{}", text); Ok(()) } $ cargo run -- -h Running `target/debug/myapp -h` Usage: myapp <PATH> Arguments: <PATH> Options: -h, --help Print help $ cargo run -- -h Running `target/debug/myapp -h` テキストを表示するCLI Usage: myapp <PATH> Arguments: <PATH> Options: -h, --help Print help -V, --version Print version
  45. CLIツール実装チートシート before after 51 Copyright © 2025, Oracle and/or its

    affiliates 2. Error を Readable に use clap::Parser; use std::fs; use anyhow::{Context, Result}; #[derive(Parser, Debug)] #[command(name="myapp", version, about="テキストを 表示するCLI")] struct Args { path: String, // ファイルパス } fn main() -> Result<()> { let args = Args::parse(); let text = fs::read_to_string(&args.path) .with_context(|| format!("ファイルを読み込 めません: {}", args.path))?; println!("読込ファイル: {}", args.path); println!("{}", text); Ok(()) } $ cargo run -- input.text Running `target/debug/myapp input.text` Error: Os { code: 2, kind: NotFound, message: "No such file or directory" } $ cargo run -- input.text Running `target/debug/myapp input.text` Error: ファイルを読み込めません: input.text ※tomlへの追加も忘れずに
  46. CLIツール実装チートシート before after 52 Copyright © 2025, Oracle and/or its

    affiliates 3. 標準入力に対応 // 略 use std::io::{self, Read}; // 略 fn read_all(path: &str) -> Result<String> { if path == "-" { let mut s = String::new(); io::stdin().read_to_string(&mut s) .context("標準入力を読み込めません")?; Ok(s) } else { Ok(fs::read_to_string(path) .with_context(|| format!("ファイルを読 み込めません: {path}"))?) } } fn main() -> Result<()> { let args = Args::parse(); let text = read_all(&args.path)?; // 略 } $ echo "Hello OCHaCafe S10 Rust" | cargo run -- - Running `target/debug/myapp -` Error: ファイルを読み込めません: - Caused by: No such file or directory (os error 2) $ echo "Hello OCHaCafe S10 Rust" | cargo run -- - Running `target/debug/myapp -` 読込ファイル: - Hello OCHaCafe S10 Rust
  47. CLIツール実装チートシート before after 53 Copyright © 2025, Oracle and/or its

    affiliates 4. ファイルの存在有無チェック // 略 use std::path::Path; use std::fs::metadata; // 略 fn validate(path: &str) -> Result<()> { if path == "-" { return Ok(()); } let p = Path::new(path); if !p.exists() { anyhow::bail!("ファイルが存在 しません: {path}"); } let meta = metadata(p).with_context(|| format!("メタデータの取得に失敗: {path}"))?; println!("{}", format!("size: {} bytes", meta.len())); Ok(()) } // 略 fn main() -> Result<()> { let args = Args::parse(); validate(&args.path)?; // 略 } $ cargo run -- input.text Running `target/debug/myapp input.text` Error: ファイルを読み込めません: input.text Caused by: No such file or directory (os error 2) $ cargo run -- input.text Running `target/debug/myapp input.text` Error: ファイルが存在しません: input.text
  48. CLIツール実装チートシート 実行結果 54 Copyright © 2025, Oracle and/or its affiliates

    5. ファイルとして結果を出力 // 略 use std::io::Write; // 略 struct Args { path: String, #[arg(long)] output: Option<String>, } // 略 fn write_out(s: &str, to: &Option<String>) -> Result<()> { if let Some(p) = to { let mut f = std::fs::File::create(p) .with_context(|| format!("出力ファイルを作成できません: {p}"))?; f.write_all(s.as_bytes()) .with_context(|| format!("出力ファイルに書き込めません: {p}"))?; eprintln!("{}", format!("ファイルに書き出しました: {p}")); } else { println!("{s}"); } Ok(()) } fn main() -> Result<()> { // 略 write_out(&text, &args.output)?; } [opc@olcd myapp]$ cargo run -- input.txt --output out.txt Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/myapp input.txt --output out.txt` size: 14 bytes ファイルに書き出しました: out.txt [opc@olcd myapp]$ cat out.txt Hello OCHaCafe
  49. CLIツール実装チートシート 実行結果 55 Copyright © 2025, Oracle and/or its affiliates

    6. --verbose オプション // 略 use env_logger::Env; // 略 struct Args { // 略 #[arg(short, long)] verbose: bool, } // 略 fn init_logger(verbose: bool) { let mut builder = env_logger::Builder::from_env(Env::default().default_filter_or("warn")); if verbose { builder.filter_level(log::LevelFilter::Info); } let _ = builder.try_init(); } fn main() -> Result<()> { let args = Args::parse(); init_logger(args.verbose); log::info!("入力を検証しています"); validate(&args.path)?; log::info!("入力を読み込みます"); let text = read_all(&args.path)?; log::info!("結果を出力します"); write_out(&text, &args.output)?; Ok(()) } $ cargo run -- input.txt -v Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/myapp input.txt -v` [2025-08-17T19:49:38Z INFO myapp] 入力を検証しています size: 14 bytes [2025-08-17T19:49:38Z INFO myapp] 入力を読み込みます [2025-08-17T19:49:38Z INFO myapp] 結果を出力します Hello OCHaCafe
  50. デモ: 分かりやすいエラー (エラーハンドリング) 実行結果 before 実行結果 after 56 Copyright ©

    2025, Oracle and/or its affiliates 「panic! をあまり使うべきでないなぁ」を体験 $ cargo run -- --grep "(" input.txt Running `target/debug/example_error --grep '(' input.txt` thread 'main' panicked at src/main.rs:16:45: called `Result::unwrap()` on an `Err` value: Syntax( ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ regex parse error: ( ^ error: unclosed group ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace $ cargo run -- --grep "(" input.txt Running `target/debug/example_error --grep '(' input.txt` Error: 正規表現が無効です: (
  51. デモ: 分かりやすいエラー (エラーハンドリング) • Regex::new(p) - Regex::new は 文字列から正規表現オブジェクトを作成する関数 -

    戻り値は Result<Regex, regex::Error> • 正しい正規表現 → Ok(Regex) • 間違った正規表現 → Err(regex::Error) • 2. .map_err(...) - map_err は エラー変換メソッド - regex::Error を直接返すのではなく、human readableなエラーメッセージに変換 - _ で元のエラーを無視しているので、正規表現ライブラリ固有の情報は出さず、シンプルなエラーに • 3. anyhow::anyhow!(...) - anyhow! マクロは 簡単に anyhow::Error を作るためのマクロ - format! のように {} プレースホルダに値を埋め込み - エラーを anyhow::Result として自然に扱えるように 57 Copyright © 2025, Oracle and/or its affiliates 実装内容を少し補足 let re = Regex::new(p).map_err(|_| anyhow::anyhow!("正規表現が無効です: {}", p))?;
  52. デモ: ユニットテスト テスト関係のマクロ • #[cfg(test)] - 「このモジュールはテスト時だけコンパイル」という属性 - cargo test

    では有効 - cargo run では無効。 • #[test] - 「この関数はテスト関数」と印をつけるマクロ mod tests { ... } • Rust ではモジュール単位でスコープを作る • ユニットテストは通常「テスト対象のソースコードと同じファイルの中」に tests モジュールを作る use super::* • テストは通常、mod tests の中に書くので一段ネストされる • そのため、テストから元の関数を呼ぶには親モジュール(super) を参 照する必要がある 58 Copyright © 2025, Oracle and/or its affiliates #[cfg(test)] mod tests { use super::*; #[test] fn grep_matches_case_insensitive() { let src = "Alice¥nalice¥nBob¥nALICE"; let pat = Some("(?i)alice".to_string()); let out = maybe_grep(src, &pat).unwrap(); assert_eq!(out.lines().count(), 3); } #[test] fn grep_none_returns_original_text() { let src = "a¥nb¥nc"; let pat = None; let out = maybe_grep(src, &pat).unwrap(); assert_eq!(out, src); } #[test] fn invalid_regex_is_human_friendly_error() { let src = "x"; let pat = Some("(".to_string()); let err = maybe_grep(src, &pat).unwrap_err(); let msg = format!("{err}"); assert!(msg.contains("正規表現が無効です")); } }
  53. デモ: WASM体験 Rust → WASM • wasm-pack build の結果、Rustのソースコードが WebAssembly

    バイナリ (simple_wasm_bg.wasm) にコンパイル • この .wasm が実際の処理を行うネイティブに近いバイトコード wasm-bindgen によるJSラッパ生成 • simple_wasm.js や simple_wasm_bg.js は、JavaScript 側から .wasm を扱いやすくするためのブリッジになるコード - fetch または import を通じて .wasm をロード - Rust関数を JavaScript 関数として呼び出せるように変換 実行時 • ブラウザ側で import * as wasm from "./simple_wasm.js" をすると - ラッパが内部で simple_wasm_bg.wasm をロード - Rustの関数がJSの関数として使えるようになる 59 Copyright © 2025, Oracle and/or its affiliates