Rust

Rust 完全ガイド — 安全性とパフォーマンスを両立するシステムプログラミング言語

1. Rust とは何か

1.1 概要

Rust は、安全性(Safety)速度(Speed)並行性(Concurrency) の3つを同時に実現することを目指して設計されたシステムプログラミング言語である。C/C++ と同等のパフォーマンスを持ちながら、コンパイル時にメモリ安全性を保証するという、従来のシステム言語では困難だった課題を解決している。

Rust の最大の特徴は 所有権システム(Ownership System) であり、ガベージコレクタ(GC)なしにメモリ安全性を実現する。これにより、ランタイムオーバーヘッドなしで、ダングリングポインタ、二重解放、データ競合といった問題をコンパイル時に検出・防止できる。

fn main() {
    println!("Hello, Rust!");
    
    // 変数はデフォルトで不変(immutable)
    let x = 5;
    // x = 6; // コンパイルエラー!
    
    // mut キーワードで可変にする
    let mut y = 10;
    y += 5;
    println!("y = {}", y); // y = 15
}

1.2 歴史

Rust の歴史は、Mozilla のエンジニアである Graydon Hoare が2006年に個人プロジェクトとして開始したことに始まる。

出来事
2006Graydon Hoare が個人プロジェクトとして Rust の開発を開始
2009Mozilla が Rust の開発を公式にスポンサー
2010Rust が初めて公に発表される
2012最初のアルファ版リリース(0.1)
2015年5月15日Rust 1.0 安定版リリース — 後方互換性の保証開始
2018Rust 2018 Edition リリース(async/await の基礎、NLL)
2020Mozilla の大規模レイオフ、Rust チームに影響
2021年2月Rust Foundation 設立(AWS, Google, Huawei, Microsoft, Mozilla が創設メンバー)
2021Rust 2021 Edition リリース
2022Linux カーネルが Rust を公式にサポート(6.1 から)
2023Windows カーネルでの Rust 採用が発表
2024Rust 2024 Edition リリース

1.3 設計哲学

1. ゼロコスト抽象化(Zero-Cost Abstractions) — 高レベルの抽象化を使用しても、手書きの低レベルコードと同等のパフォーマンスを発揮する。

let sum: i32 = (1..=100).filter(|x| x % 2 == 0).map(|x| x * x).sum();

2. メモリ安全性の保証(Memory Safety without GC) — 所有権システムにより、GC なしにメモリ安全性を保証する。

3. データ競合の防止(Fearless Concurrency) — 型システムと所有権システムにより、データ競合をコンパイル時に検出する。

4. 明示性(Explicitness) — 暗黙の動作を最小限にし、プログラマが意図を明確に表現することを促す。

1.4 Mozilla からの独立と Rust Foundation

2021年2月8日、Rust Foundation が正式に設立された。創設メンバーは AWS、Google、Huawei、Microsoft、Mozilla の5社である。Rust Foundation は、Rust 言語の開発、エコシステムの維持、コミュニティの支援を担う非営利団体として機能している。

1.5 Rust が選ばれる理由

企業/プロジェクト用途
Linux カーネルデバイスドライバ、カーネルモジュール
AndroidOS コンポーネント、Bluetooth スタック
AWSFirecracker(マイクロVM)、Bottlerocket(コンテナOS)
CloudflarePingora(HTTP プロキシ)
Discordメッセージング基盤の高性能化
Dropboxファイル同期エンジン
Metaソースコード管理(Mononoke)
MicrosoftWindows カーネルコンポーネント

2. 所有権システム(Ownership System)

Rust の最も革新的な特徴は 所有権システム である。コンパイル時にメモリの安全性を保証する仕組みであり、GC のランタイムコストなしにダングリングポインタ、二重解放、メモリリークを防止する。

2.1 所有権の基本ルール

  1. Rust の各値は、所有者(owner)と呼ばれる変数を持つ
  2. 各値の所有者は同時に1つだけ
  3. 所有者がスコープを抜けると、値は破棄(drop)される
fn main() {
    let s = String::from("hello");   // ルール1: s が所有者
    let s2 = s;                       // ルール2: 所有権が s2 に移動(move)
    // println!("{}", s);             // コンパイルエラー!s はもう無効
    println!("{}", s2);               // OK
} // ルール3: s2 がスコープを抜け、メモリが解放される

2.2 ムーブセマンティクス(Move Semantics)

fn take_ownership(s: String) {
    println!("受け取った文字列: {}", s);
}

fn main() {
    let my_string = String::from("hello");
    take_ownership(my_string);
    // println!("{}", my_string); // コンパイルエラー!所有権はすでに移動済み
    
    // Copy トレイトを実装する型はコピーされる
    let x = 42;
    let y = x; // コピーが発生
    println!("x = {}, y = {}", x, y); // 両方使用可能
}

2.3 参照と借用(References and Borrowing)

fn calculate_length(s: &String) -> usize { s.len() }

fn append_world(s: &mut String) { s.push_str(", world!"); }

fn main() {
    let mut my_string = String::from("hello");
    let len = calculate_length(&my_string);       // 不変参照
    println!("'{}' の長さは {} です", my_string, len);
    append_world(&mut my_string);                  // 可変参照
    println!("{}", my_string);
}

2.4 借用のルール

  1. 任意の時点で、1つの可変参照 OR 任意の数の不変参照のいずれか一方のみ存在できる
  2. 参照は常に有効でなければならない(ダングリング参照の禁止)
fn main() {
    let mut s = String::from("hello");
    let r1 = &s;
    let r2 = &s;
    println!("{} and {}", r1, r2);
    // r1, r2 はここ以降使用されないため、可変参照が可能
    let r3 = &mut s;
    r3.push_str(", world!");
    println!("{}", r3);
}

2.5 ライフタイム(Lifetimes)

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

// 構造体のライフタイム
struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("お知らせ: {}", announcement);
        self.part
    }
}

2.6 ライフタイムの省略規則

コンパイラは3つの規則でライフタイムを自動推論する:

  1. 各参照パラメータに個別のライフタイムが割り当てられる
  2. 入力ライフタイムが1つだけの場合、全出力に適用
  3. メソッドの場合、self のライフタイムが全出力に適用

2.7 'static ライフタイム

let s: &'static str = "この文字列はプログラム全体で有効";

fn make_static_string() -> &'static str {
    let s = String::from("leaked string");
    Box::leak(s.into_boxed_str())
}

3. 型システム

3.1 基本型(Primitive Types)

fn main() {
    let i32_val: i32 = 42;          // 32ビット符号付き(デフォルト)
    let f64_val: f64 = 2.71828;     // 64ビット浮動小数点(デフォルト)
    let is_active: bool = true;
    let c: char = '🦀';             // Unicode スカラー値、4バイト
    let unit: () = ();               // ユニット型
    let tuple: (i32, f64, &str) = (42, 3.14, "hello");
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
    let slice: &[i32] = &arr[1..3];
}

3.2 構造体(Structs)

#[derive(Debug, Clone)]
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

impl User {
    fn new(username: String, email: String) -> Self {
        Self { username, email, sign_in_count: 0, active: true }
    }
    fn full_info(&self) -> String {
        format!("{} <{}> (ログイン回数: {})", self.username, self.email, self.sign_in_count)
    }
    fn increment_sign_in(&mut self) { self.sign_in_count += 1; }
    fn deactivate(self) -> User { User { active: false, ..self } }
}

struct Color(u8, u8, u8);  // タプル構造体
struct Marker;              // ユニット構造体

3.3 列挙型(Enums)

#[derive(Debug)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

impl Message {
    fn process(&self) {
        match self {
            Message::Quit => println!("終了"),
            Message::Move { x, y } => println!("移動: ({}, {})", x, y),
            Message::Write(text) => println!("書き込み: {}", text),
            Message::ChangeColor(r, g, b) => println!("色変更: RGB({}, {}, {})", r, g, b),
        }
    }
}

fn find_user(id: u64) -> Option<String> {
    if id == 1 { Some(String::from("taro")) } else { None }
}

3.4 ジェネリクス(Generics)

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in &list[1..] { if item > largest { largest = item; } }
    largest
}

#[derive(Debug)]
struct Point<T> { x: T, y: T }

impl<T> Point<T> { fn x(&self) -> &T { &self.x } }
impl Point<f64> {
    fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() }
}

3.5 トレイト(Traits)

trait Summary {
    fn summarize_author(&self) -> String;
    fn summarize(&self) -> String {
        format!("({}さんの記事をもっと読む...)", self.summarize_author())
    }
}

struct Tweet { username: String, content: String }
impl Summary for Tweet {
    fn summarize_author(&self) -> String { format!("@{}", self.username) }
}

fn notify(item: &impl Summary) { println!("速報! {}", item.summarize()); }

fn process_items<T, U>(t: &T, u: &U) -> String
where T: Summary + Clone, U: Summary + std::fmt::Debug,
{ format!("{} / {:?}", t.summarize(), u) }

fn create_summarizable() -> impl Summary {
    Tweet { username: String::from("rustlang"), content: String::from("Rust!") }
}

3.6 関連型(Associated Types)

struct Counter { count: u32, max: u32 }
impl Counter { fn new(max: u32) -> Self { Counter { count: 0, max } } }

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        if self.count < self.max { self.count += 1; Some(self.count) } else { None }
    }
}

3.7 トレイトオブジェクト(動的ディスパッチ)

trait Animal {
    fn name(&self) -> &str;
    fn sound(&self) -> &str;
    fn info(&self) -> String { format!("{} は「{}」と鳴きます", self.name(), self.sound()) }
}

struct Dog { name: String }
struct Cat { name: String }
impl Animal for Dog { fn name(&self) -> &str { &self.name } fn sound(&self) -> &str { "ワン" } }
impl Animal for Cat { fn name(&self) -> &str { &self.name } fn sound(&self) -> &str { "ニャー" } }

fn get_animals() -> Vec<Box<dyn Animal>> {
    vec![
        Box::new(Dog { name: String::from("ポチ") }),
        Box::new(Cat { name: String::from("タマ") }),
    ]
}

3.8 型エイリアスとニュータイプパターン

type Kilometers = i32;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

struct Wrapper(Vec<String>);
impl std::fmt::Display for Wrapper {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

4. エラーハンドリング

Rust は例外機構を持たず、Result<T, E>Option<T> 型を使った明示的なエラーハンドリングを採用している。

4.1 panic! と Result 型

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

fn read_file_content(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    Ok(content)
}

fn main() {
    match read_file_content("config.txt") {
        Ok(content) => println!("内容: {}", content),
        Err(e) => eprintln!("エラー: {}", e),
    }
    
    let content = read_file_content("config.txt")
        .unwrap_or_else(|_| String::from("デフォルト設定"));
}

4.2 ? 演算子の詳細

use std::num::ParseIntError;

fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
    let n = s.parse::<i32>()?;
    Ok(n * 2)
}

#[derive(Debug)]
enum AppError {
    Io(std::io::Error),
    Parse(ParseIntError),
}
impl From<std::io::Error> for AppError {
    fn from(e: std::io::Error) -> Self { AppError::Io(e) }
}
impl From<ParseIntError> for AppError {
    fn from(e: ParseIntError) -> Self { AppError::Parse(e) }
}

fn complex_operation(path: &str) -> Result<i32, AppError> {
    let content = std::fs::read_to_string(path)?;
    let number = content.trim().parse::<i32>()?;
    Ok(number * 2)
}

4.3 カスタムエラー型

use std::fmt;

#[derive(Debug)]
enum DatabaseError {
    ConnectionFailed(String),
    QueryFailed { query: String, reason: String },
    RecordNotFound(u64),
    Timeout(std::time::Duration),
}

impl fmt::Display for DatabaseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DatabaseError::ConnectionFailed(url) => write!(f, "接続失敗: {}", url),
            DatabaseError::QueryFailed { query, reason } => write!(f, "クエリ '{}' 失敗: {}", query, reason),
            DatabaseError::RecordNotFound(id) => write!(f, "ID {} が見つかりません", id),
            DatabaseError::Timeout(d) => write!(f, "タイムアウト: {:?}", d),
        }
    }
}
impl std::error::Error for DatabaseError {}

4.4 thiserror と anyhow クレート

[dependencies]
thiserror = "2"
anyhow = "1"
use thiserror::Error;

#[derive(Error, Debug)]
enum ApiError {
    #[error("HTTP リクエスト失敗: {0}")]
    HttpError(#[from] reqwest::Error),
    #[error("JSON パースエラー: {0}")]
    JsonError(#[from] serde_json::Error),
    #[error("認証エラー: ユーザー '{username}' は権限がありません")]
    Unauthorized { username: String },
}

// anyhow: アプリケーション向け
use anyhow::{Context, Result, bail, ensure};

fn load_config(path: &str) -> Result<Config> {
    let content = std::fs::read_to_string(path)
        .context(format!("設定ファイル '{}' の読み込みに失敗", path))?;
    let config: Config = serde_json::from_str(&content)
        .context("JSON パースに失敗")?;
    ensure!(!config.name.is_empty(), "name は空にできません");
    if config.port == 0 { bail!("ポート番号が無効です"); }
    Ok(config)
}

4.5 Option 型の活用

fn main() {
    let some_value: Option<i32> = Some(42);
    let doubled = some_value.map(|x| x * 2);           // Some(84)
    let result = some_value.and_then(|x| if x > 0 { Some(x) } else { None });
    let filtered = some_value.filter(|&x| x > 100);    // None
    let val = some_value.unwrap_or(0);
    let zipped = Some(1).zip(Some("hello"));            // Some((1, "hello"))
    let flat = Some(Some(42)).flatten();                 // Some(42)
    let result: Result<i32, &str> = some_value.ok_or("値がありません");
}

5. パターンマッチング

5.1 match 式

#[derive(Debug)]
enum Coin { Penny, Nickel, Dime, Quarter(UsState) }
#[derive(Debug)]
enum UsState { Alabama, Alaska, California }

fn value_in_cents(coin: &Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => { println!("{:?} 州", state); 25 }
    }
}

5.2 パターンの種類

fn pattern_examples() {
    let x = 5;
    match x {
        1..=5 => println!("1から5"),
        6..=10 => println!("6から10"),
        _ => println!("範囲外"),
    }
    
    match x {
        1 | 3 | 5 | 7 | 9 => println!("奇数"),
        2 | 4 | 6 | 8 | 10 => println!("偶数"),
        _ => println!("範囲外"),
    }
    
    struct Point { x: i32, y: i32 }
    let point = Point { x: 0, y: 7 };
    match point {
        Point { x: 0, y } => println!("x軸上: y={}", y),
        Point { x, y: 0 } => println!("y軸上: x={}", x),
        Point { x, y } => println!("({}, {})", x, y),
    }
    
    // ガード条件
    let num = Some(4);
    match num {
        Some(x) if x < 5 => println!("5未満: {}", x),
        Some(x) => println!("5以上: {}", x),
        None => println!("なし"),
    }
    
    // @ バインディング
    match x {
        n @ 1..=5 => println!("1〜5: {}", n),
        n => println!("範囲外: {}", n),
    }
}

5.3 if let, while let, let else

fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max { println!("最大値: {}", max); }
    
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() { println!("{}", top); }
    
    // let ... else(Rust 1.65+)
    fn process_input(input: &str) -> Result<u32, String> {
        let Ok(number) = input.parse::<u32>() else {
            return Err(format!("'{}' は数値ではありません", input));
        };
        Ok(number * 2)
    }
}

5.4 網羅性チェック

enum TrafficLight { Red, Yellow, Green }

fn action(light: TrafficLight) -> &'static str {
    match light {
        TrafficLight::Red => "止まれ",
        TrafficLight::Yellow => "注意",
        TrafficLight::Green => "進め",
    }
}

6. コレクション

6.1 Vec

fn main() {
    let mut v = Vec::new();
    v.push(1); v.push(2); v.push(3);
    let v2 = vec![1, 2, 3, 4, 5];
    
    let third = v2.get(2); // Option<&T>
    for val in &v2 { println!("{}", val); }
    
    let mut numbers = vec![1, 2, 3];
    for val in &mut numbers { *val *= 2; }
    
    let v = vec![3, 1, 4, 1, 5, 9, 2, 6];
    let mut sorted = v.clone();
    sorted.sort();
    sorted.dedup();
    
    let mut evens = vec![1, 2, 3, 4, 5, 6];
    evens.retain(|&x| x % 2 == 0); // [2, 4, 6]
}

6.2 HashMap<K, V>

use std::collections::HashMap;

fn main() {
    let mut scores: HashMap<String, i32> = HashMap::new();
    scores.insert(String::from("Alice"), 100);
    scores.entry(String::from("Bob")).or_insert(85);
    
    // ワードカウント
    let text = "hello world wonderful world";
    let mut word_count: HashMap<&str, u32> = HashMap::new();
    for word in text.split_whitespace() {
        *word_count.entry(word).or_insert(0) += 1;
    }
}

6.3 BTreeMap, HashSet, BTreeSet

use std::collections::{BTreeMap, HashSet, BTreeSet};

fn main() {
    let mut btree: BTreeMap<&str, i32> = BTreeMap::new();
    btree.insert("banana", 3);
    btree.insert("apple", 5);
    // キー順でイテレーション: apple, banana
    
    let a: HashSet<i32> = [1, 2, 3, 4, 5].into();
    let b: HashSet<i32> = [3, 4, 5, 6, 7].into();
    let union: HashSet<_> = a.union(&b).collect();
    let intersection: HashSet<_> = a.intersection(&b).collect();
    let difference: HashSet<_> = a.difference(&b).collect();
    
    let sorted_set: BTreeSet<i32> = [5, 3, 1, 4, 2].into(); // {1, 2, 3, 4, 5}
}

7. イテレータとクロージャ

7.1 クロージャ(Closures)

fn main() {
    let add = |a, b| a + b;
    println!("3 + 4 = {}", add(3, 4));
    
    let name = String::from("Rust");
    let greet = || println!("Hello, {}!", name); // Fn: 不変借用
    greet();
    
    let mut count = 0;
    let mut increment = || { count += 1; count }; // FnMut: 可変借用
    println!("{}", increment());
    
    let name = String::from("World");
    let consume = move || println!("Consumed: {}", name); // FnOnce: 所有権移動
    consume();
    
    fn make_adder(x: i32) -> impl Fn(i32) -> i32 { move |y| x + y }
    let add_five = make_adder(5);
    println!("5 + 3 = {}", add_five(3));
}

7.2 イテレータ

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    
    let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
    let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
    let sum = numbers.iter().fold(0, |acc, &x| acc + x);
    
    let parsed: Vec<i32> = vec!["1", "abc", "3"]
        .into_iter().filter_map(|s| s.parse().ok()).collect();
    
    let words: Vec<&str> = vec!["hello world", "foo bar"]
        .iter().flat_map(|s| s.split_whitespace()).collect();
    
    let (evens, odds): (Vec<_>, Vec<_>) = numbers.iter().partition(|&&x| x % 2 == 0);
    
    println!("sum: {}", numbers.iter().sum::<i32>());
    println!("any even: {}", numbers.iter().any(|&x| x % 2 == 0));
    println!("find >5: {:?}", numbers.iter().find(|&&x| x > 5));
}

7.3 カスタムイテレータ

struct Fibonacci { a: u64, b: u64 }
impl Fibonacci { fn new() -> Self { Fibonacci { a: 0, b: 1 } } }
impl Iterator for Fibonacci {
    type Item = u64;
    fn next(&mut self) -> Option<Self::Item> {
        let result = self.a;
        let new_b = self.a + self.b;
        self.a = self.b;
        self.b = new_b;
        Some(result)
    }
}

fn main() {
    let fibs: Vec<u64> = Fibonacci::new().take(10).collect();
    // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
}

8. スマートポインタ

8.1 Box

fn main() {
    let b = Box::new(5);
    
    #[derive(Debug)]
    enum List { Cons(i32, Box<List>), Nil }
    
    let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
}

8.2 Rc(参照カウント)

use std::rc::Rc;

fn main() {
    let shared = Rc::new(String::from("共有データ"));
    let clone1 = Rc::clone(&shared);
    println!("参照カウント: {}", Rc::strong_count(&shared)); // 2
}

8.3 RefCell(内部可変性)

use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let data = RefCell::new(vec![1, 2, 3]);
    data.borrow_mut().push(4);
    println!("{:?}", data.borrow());
    
    // Rc<RefCell<T>> パターン: 共有かつ可変
    let shared = Rc::new(RefCell::new(String::from("hello")));
    Rc::clone(&shared).borrow_mut().push_str(" world");
}

8.4 Arc と Mutex

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            *counter.lock().unwrap() += 1;
        }));
    }
    for handle in handles { handle.join().unwrap(); }
    println!("結果: {}", *counter.lock().unwrap()); // 10
}

8.5 Cow(Clone on Write)

use std::borrow::Cow;

fn remove_whitespace(input: &str) -> Cow<str> {
    if input.contains(' ') { Cow::Owned(input.replace(' ', "")) }
    else { Cow::Borrowed(input) }
}

9. 並行処理

9.1 スレッド

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=5 { println!("スレッド: {}", i); thread::sleep(Duration::from_millis(100)); }
        42
    });
    let result = handle.join().unwrap(); // 42
    
    // スコープ付きスレッド(Rust 1.63+)
    let mut data = vec![1, 2, 3];
    thread::scope(|s| {
        s.spawn(|| println!("読み取り: {:?}", &data));
    });
    data.push(4);
}

9.2 チャネル

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        for msg in vec!["hello", "from", "thread"] { tx.send(msg).unwrap(); }
    });
    for received in rx { println!("受信: {}", received); }
}

9.3 async/await と tokio

[dependencies]
tokio = { version = "1", features = ["full"] }
use tokio::time::{sleep, Duration};

async fn fetch_data(id: u32) -> String {
    sleep(Duration::from_millis(100)).await;
    format!("データ #{}", id)
}

async fn process_all() {
    let (d1, d2, d3) = tokio::join!(fetch_data(1), fetch_data(2), fetch_data(3));
    println!("{}, {}, {}", d1, d2, d3);
    
    tokio::select! {
        val = fetch_data(1) => println!("最初に完了: {}", val),
        val = fetch_data(2) => println!("最初に完了: {}", val),
    }
}

#[tokio::main]
async fn main() { process_all().await; }

10. unsafe Rust

10.1 unsafe で可能になる操作

fn main() {
    let mut num = 5;
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;
    unsafe {
        println!("r1: {}", *r1);
        *r2 = 10;
    }
    
    static mut COUNTER: u32 = 0;
    unsafe { COUNTER += 1; }
}

fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = values.len();
    let ptr = values.as_mut_ptr();
    assert!(mid <= len);
    unsafe {
        (std::slice::from_raw_parts_mut(ptr, mid),
         std::slice::from_raw_parts_mut(ptr.add(mid), len - mid))
    }
}

10.2 FFI(Foreign Function Interface)

extern "C" { fn abs(input: i32) -> i32; }

fn main() { unsafe { println!("abs(-3) = {}", abs(-3)); } }

#[no_mangle]
pub extern "C" fn rust_function(x: i32) -> i32 { x * 2 }

10.3 unsafe トレイト

unsafe trait Trustworthy { fn validate(&self) -> bool; }
struct SafeData { value: i32 }
unsafe impl Trustworthy for SafeData {
    fn validate(&self) -> bool { self.value >= 0 }
}
// Send, Sync は unsafe トレイト。ほとんどの型は自動実装。
// Rc<T> は Send でも Sync でもない(Arc<T> を使用)。

11. マクロシステム

11.1 宣言的マクロ

macro_rules! vec_of_strings {
    ($($element:expr),* $(,)?) => {{
        let mut v = Vec::new();
        $(v.push(String::from($element));)*
        v
    }};
}

macro_rules! hashmap {
    ($($key:expr => $value:expr),* $(,)?) => {{
        let mut map = std::collections::HashMap::new();
        $(map.insert($key, $value);)*
        map
    }};
}

fn main() {
    let names = vec_of_strings!["Alice", "Bob"];
    let scores = hashmap! { "Alice" => 100, "Bob" => 85 };
}

11.2 手続き的マクロ

[lib]
proc-macro = true
[dependencies]
syn = { version = "2", features = ["full"] }
quote = "1"
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;
    let expanded = quote! {
        impl HelloMacro for #name {
            fn hello_macro() { println!("Hello, {}!", stringify!(#name)); }
        }
    };
    TokenStream::from(expanded)
}

11.3 よく使われる derive マクロ

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
struct Point { x: i32, y: i32 }

// serde の derive
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct Config {
    #[serde(rename = "serverName")]
    server_name: String,
    #[serde(default = "default_port")]
    port: u16,
    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option<String>,
}
fn default_port() -> u16 { 8080 }

12. Cargo の詳細設定

12.1 Cargo.toml

[package]
name = "my-project"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
license = "MIT OR Apache-2.0"

[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
my-lib = { git = "https://github.com/user/my-lib", branch = "main" }
shared = { path = "../shared" }
fancy-logging = { version = "0.1", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winuser"] }

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
mockall = "0.12"
tempfile = "3"

[build-dependencies]
cc = "1"

[features]
default = ["json", "logging"]
json = ["dep:serde", "dep:serde_json"]
logging = ["dep:fancy-logging"]

[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
strip = true

12.2 ワークスペース

[workspace]
resolver = "2"
members = ["crates/core", "crates/api", "crates/cli"]

[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"
thiserror = "2"

[workspace.package]
version = "0.1.0"
edition = "2021"
# crates/core/Cargo.toml
[package]
name = "my-core"
version.workspace = true
edition.workspace = true
[dependencies]
serde.workspace = true
thiserror.workspace = true

12.3 ビルドスクリプト

// build.rs
fn main() {
    println!("cargo:rustc-env=GIT_HASH={}", get_git_hash());
    println!("cargo:rerun-if-changed=src/native/helper.c");
    cc::Build::new().file("src/native/helper.c").compile("helper");
}

fn get_git_hash() -> String {
    std::process::Command::new("git")
        .args(["rev-parse", "--short", "HEAD"])
        .output().ok()
        .and_then(|o| String::from_utf8(o.stdout).ok())
        .unwrap_or_else(|| "unknown".to_string())
        .trim().to_string()
}

12.4 主要な Cargo コマンド

cargo new my-project           # バイナリプロジェクト作成
cargo build --release          # リリースビルド
cargo run -- arg1 arg2         # 引数付き実行
cargo test                     # テスト実行
cargo check                    # コンパイルチェック
cargo clippy                   # リント
cargo fmt                      # フォーマット
cargo doc --open               # ドキュメント生成
cargo tree                     # 依存関係ツリー
cargo audit                    # セキュリティ脆弱性チェック

13. テスト

13.1 単体テスト

pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 { Err("ゼロ除算".to_string()) } else { Ok(a / b) }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_add() { assert_eq!(add(2, 3), 5); }
    
    #[test]
    fn test_divide_by_zero() {
        assert!(divide(10.0, 0.0).is_err());
    }
    
    #[test]
    #[should_panic(expected = "index out of bounds")]
    fn test_panic() { let v = vec![1, 2, 3]; let _ = v[10]; }
    
    #[test]
    fn test_result() -> Result<(), String> {
        let result = divide(10.0, 2.0)?;
        assert_eq!(result, 5.0);
        Ok(())
    }
    
    #[test]
    #[ignore = "長時間かかるテスト"]
    fn expensive_test() { std::thread::sleep(std::time::Duration::from_secs(10)); }
}

13.2 統合テスト

// tests/integration_test.rs
use my_project::{add, Calculator};

#[test]
fn test_full_workflow() {
    let mut calc = Calculator::new();
    assert_eq!(calc.add(10.0, 20.0), 30.0);
    assert_eq!(calc.history().len(), 1);
}

13.3 ドキュメントテスト

/// 2つの数値を加算します。
/// ```
/// assert_eq!(my_project::add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 { a + b }

13.4 プロパティベーステスト

#[cfg(test)]
mod property_tests {
    use proptest::prelude::*;
    proptest! {
        #[test]
        fn test_double_reverse(ref v in prop::collection::vec(any::<i32>(), 0..100)) {
            let reversed_twice: Vec<_> = v.iter().rev().rev().cloned().collect();
            prop_assert_eq!(v, &reversed_twice);
        }
    }
}

13.5 モック

use mockall::{automock, predicate::*};

#[automock]
trait Database {
    fn get_user(&self, id: u64) -> Option<String>;
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_mock() {
        let mut mock = MockDatabase::new();
        mock.expect_get_user().with(eq(1)).returning(|_| Some("Alice".to_string()));
        assert_eq!(mock.get_user(1), Some("Alice".to_string()));
    }
}

14. クレートエコシステム

カテゴリクレート説明
シリアライズserde + serde_jsonデファクトスタンダードのシリアライズフレームワーク
非同期ランタイムtokio最も広く使われる非同期ランタイム
Web フレームワークaxumtokio ベースの Web フレームワーク
Web フレームワークactix-web高性能 Web フレームワーク
HTTP クライアントreqwest高レベル HTTP クライアント
CLIclapコマンドライン引数パーサー
エラー(ライブラリ)thiserrorカスタムエラー型の derive マクロ
エラー(アプリ)anyhow柔軟なエラーハンドリング
ログtracing構造化ログ・分散トレーシング
データベースsqlxコンパイル時 SQL チェック付き非同期 DB ドライバ
ORMdiesel / sea-orm型安全な ORM
正規表現regex高性能正規表現エンジン
日付・時刻chrono / time日付・時刻ライブラリ

14.1 serde の活用

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", content = "payload")]
enum Event {
    #[serde(rename = "user_created")]
    UserCreated { id: u64, name: String },
    #[serde(rename = "order_placed")]
    OrderPlaced { order_id: String, amount: f64 },
}

14.2 clap による CLI

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "filetool", about = "ファイル操作 CLI")]
struct Cli {
    #[arg(short, long)]
    verbose: bool,
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    Search { pattern: String, #[arg(short, long, default_value = ".")] directory: String },
    Stats { path: String },
}

15. Web アプリケーション開発

15.1 Axum

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
tower-http = { version = "0.5", features = ["cors", "trace"] }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres"] }
use axum::{extract::{Json, Path, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, Router};
use serde::{Deserialize, Serialize};

#[derive(Clone)]
struct AppState { db: sqlx::PgPool }

#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
struct User { id: uuid::Uuid, name: String, email: String }

#[derive(Deserialize)]
struct CreateUserRequest { name: String, email: String }

async fn list_users(State(state): State<AppState>) -> impl IntoResponse {
    let users = sqlx::query_as::<_, User>("SELECT * FROM users")
        .fetch_all(&state.db).await.unwrap();
    Json(users)
}

async fn create_user(State(state): State<AppState>, Json(payload): Json<CreateUserRequest>) -> impl IntoResponse {
    let user = sqlx::query_as::<_, User>(
        "INSERT INTO users (id, name, email) VALUES ($1, $2, $3) RETURNING *")
        .bind(uuid::Uuid::new_v4()).bind(&payload.name).bind(&payload.email)
        .fetch_one(&state.db).await.unwrap();
    (StatusCode::CREATED, Json(user))
}

fn create_router(state: AppState) -> Router {
    Router::new()
        .route("/api/users", get(list_users).post(create_user))
        .with_state(state)
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = sqlx::PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
    let app = create_router(AppState { db });
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
    axum::serve(listener, app).await?;
    Ok(())
}

15.2 Actix-web

use actix_web::{web, App, HttpServer, HttpResponse};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Item { id: u64, name: String }

async fn get_items() -> HttpResponse {
    HttpResponse::Ok().json(vec![Item { id: 1, name: "Widget".into() }])
}

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

16. 組み込み開発

16.1 no_std 環境

#![no_std]
#![no_main]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! { loop {} }

#[no_mangle]
pub extern "C" fn _start() -> ! { loop {} }

16.2 embedded-hal

[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = "1.0"
stm32f4xx-hal = { version = "0.21", features = ["stm32f411"] }
panic-halt = "0.2"
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::{pac, prelude::*};

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();
    let gpioa = dp.GPIOA.split();
    let mut led = gpioa.pa5.into_push_pull_output();
    let cp = cortex_m::Peripherals::take().unwrap();
    let mut delay = cp.SYST.delay(&clocks);
    loop { led.set_high(); delay.delay_ms(500u32); led.set_low(); delay.delay_ms(500u32); }
}

17. WebAssembly

[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["console", "Document", "Element", "Window"] }
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn greet(name: &str) -> String { format!("Hello, {}! (from Rust/WASM)", name) }

#[wasm_bindgen]
pub struct Calculator { history: Vec<f64> }

#[wasm_bindgen]
impl Calculator {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self { Calculator { history: vec![] } }
    pub fn add(&mut self, a: f64, b: f64) -> f64 {
        let r = a + b; self.history.push(r); r
    }
}
wasm-pack build --target web
import init, { greet, Calculator } from './pkg/my_wasm_lib.js';
async function main() {
    await init();
    console.log(greet("World"));
    const calc = new Calculator();
    console.log(calc.add(1, 2)); // 3
}
main();

18. メモリレイアウトとパフォーマンス最適化

18.1 型のメモリレイアウト

use std::mem;

fn main() {
    println!("i32: {} bytes", mem::size_of::<i32>());       // 4
    println!("&str: {} bytes", mem::size_of::<&str>());      // 16
    println!("String: {} bytes", mem::size_of::<String>());  // 24
    println!("Vec<i32>: {} bytes", mem::size_of::<Vec<i32>>()); // 24
    
    // Option のニッチ最適化
    println!("Option<&i32>: {} bytes", mem::size_of::<Option<&i32>>()); // 8(追加コストなし!)
    println!("Option<Box<i32>>: {} bytes", mem::size_of::<Option<Box<i32>>>()); // 8
    
    // 構造体のパディング
    struct A { a: u8, b: u64, c: u8 }  // 24 bytes
    struct B { b: u64, a: u8, c: u8 }  // 16 bytes(フィールド順最適化)
}

18.2 パフォーマンス最適化テクニック

// 事前にキャパシティを確保
let mut s = String::with_capacity(4000);
let mut v = Vec::with_capacity(1000);

// Cow で不要なクローンを回避
use std::borrow::Cow;
fn process_name(name: &str) -> Cow<str> {
    if name.contains(' ') { Cow::Owned(name.replace(' ', "_")) }
    else { Cow::Borrowed(name) }
}

18.3 ベンチマーク(criterion)

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "my_benchmark"
harness = false
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn bench_fibonacci(c: &mut Criterion) {
    c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}

criterion_group!(benches, bench_fibonacci);
criterion_main!(benches);

19. Rust のコンパイラアーキテクチャ

19.1 コンパイルパイプライン

ソースコード (.rs) → 字句解析 → AST → マクロ展開 → HIR → 借用チェック
→ MIR → MIR最適化 → 単相化 → LLVM IR → LLVM バックエンド → ネイティブコード → リンキング

19.2 MIR

cargo rustc -- --emit=mir      # MIR の出力
cargo rustc -- --emit=llvm-ir  # LLVM IR の出力
cargo rustc -- --emit=asm      # アセンブリ出力

19.3 クロスコンパイル

rustup target add aarch64-unknown-linux-gnu
cargo build --target aarch64-unknown-linux-gnu

20. ツールチェーン

20.1 rustup

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update
rustup default stable
rustup target add wasm32-unknown-unknown
rustup component add clippy rustfmt rust-analyzer miri
# rust-toolchain.toml
[toolchain]
channel = "1.78.0"
components = ["rustfmt", "clippy", "rust-analyzer"]
targets = ["wasm32-unknown-unknown"]

20.2 clippy

cargo clippy --all-targets --all-features
cargo clippy --fix
#![warn(clippy::pedantic)]
#![deny(clippy::unwrap_used)]

20.3 rustfmt

# rustfmt.toml
max_width = 100
tab_spaces = 4
edition = "2021"
imports_granularity = "Crate"
group_imports = "StdExternalCrate"

20.4 miri

rustup +nightly component add miri
cargo +nightly miri test

21. CI/CD 設定

21.1 GitHub Actions

name: CI
on: [push, pull_request]
env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-Dwarnings"

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo check --all-targets --all-features

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo test --all-features

  fmt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with: { components: rustfmt }
      - run: cargo fmt --all -- --check

  clippy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with: { components: clippy }
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --all-targets -- -D warnings

  release:
    if: startsWith(github.ref, 'refs/tags/v')
    needs: [test, fmt, clippy]
    strategy:
      matrix:
        include:
          - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
          - { target: x86_64-apple-darwin, os: macos-latest }
          - { target: aarch64-apple-darwin, os: macos-latest }
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: "${{ matrix.target }}" }
      - run: cargo build --release --target ${{ matrix.target }}

21.2 Docker マルチステージビルド

FROM rust:1.78-slim as builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src
COPY src/ src/
RUN touch src/main.rs && cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/my-app /usr/local/bin/
EXPOSE 3000
CMD ["my-app"]

22. 実践例

22.1 CLI ツール

[dependencies]
clap = { version = "4", features = ["derive"] }
csv = "1"
serde = { version = "1", features = ["derive"] }
anyhow = "1"
comfy-table = "7"
use anyhow::{Context, Result};
use clap::Parser;
use comfy_table::Table;
use std::path::PathBuf;

#[derive(Parser)]
#[command(name = "csv-analyzer")]
struct Args {
    file: PathBuf,
    #[arg(short, long, default_value_t = 10)]
    limit: usize,
    #[arg(short, long)]
    stats: bool,
}

fn main() -> Result<()> {
    let args = Args::parse();
    let mut reader = csv::Reader::from_path(&args.file)
        .context("ファイルを開けません")?;
    let headers: Vec<String> = reader.headers()?.iter().map(|h| h.to_string()).collect();
    let records: Vec<csv::StringRecord> = reader.records().collect::<Result<_, _>>()?;
    
    let mut table = Table::new();
    table.set_header(&headers);
    for record in records.iter().take(args.limit) {
        table.add_row(record.iter().collect::<Vec<_>>());
    }
    println!("{}", table);
    Ok(())
}

22.2 ライブラリ

use parking_lot::Mutex;
use std::collections::HashMap;
use std::time::{Duration, Instant};

pub struct RateLimiter {
    max_requests: usize,
    window: Duration,
    state: Mutex<HashMap<String, Vec<Instant>>>,
}

impl RateLimiter {
    pub fn new(max_requests: usize, window: Duration) -> Self {
        RateLimiter { max_requests, window, state: Mutex::new(HashMap::new()) }
    }
    
    pub fn try_acquire(&self, key: &str) -> bool {
        let now = Instant::now();
        let mut state = self.state.lock();
        let timestamps = state.entry(key.to_string()).or_default();
        timestamps.retain(|&t| now.duration_since(t) < self.window);
        if timestamps.len() < self.max_requests { timestamps.push(now); true } else { false }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_rate_limiting() {
        let limiter = RateLimiter::new(3, Duration::from_secs(60));
        assert!(limiter.try_acquire("user1"));
        assert!(limiter.try_acquire("user1"));
        assert!(limiter.try_acquire("user1"));
        assert!(!limiter.try_acquire("user1")); // 4回目は拒否
    }
}

22.3 Todo API サーバー

use axum::{extract::{Json, Path, State}, http::StatusCode, response::IntoResponse, routing::{get, post, put, delete}, Router};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Todo { id: String, title: String, completed: bool }

type Db = Arc<RwLock<HashMap<String, Todo>>>;

async fn list_todos(State(db): State<Db>) -> impl IntoResponse {
    Json(db.read().await.values().cloned().collect::<Vec<_>>())
}

async fn create_todo(State(db): State<Db>, Json(input): Json<serde_json::Value>) -> impl IntoResponse {
    let todo = Todo {
        id: uuid::Uuid::new_v4().to_string(),
        title: input["title"].as_str().unwrap_or("").to_string(),
        completed: false,
    };
    db.write().await.insert(todo.id.clone(), todo.clone());
    (StatusCode::CREATED, Json(todo))
}

#[tokio::main]
async fn main() {
    let db: Db = Arc::new(RwLock::new(HashMap::new()));
    let app = Router::new()
        .route("/todos", get(list_todos).post(create_todo))
        .with_state(db);
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

23. まとめ

Rust は、メモリ安全性とパフォーマンスを両立する革新的なプログラミング言語である。所有権システム、強力な型システム、ゼロコスト抽象化により、安全で効率的なソフトウェアを構築できる。

Rust の強み

  1. コンパイル時のメモリ安全性 — GC なしでダングリングポインタ、二重解放、データ競合を防止
  2. ゼロコスト抽象化 — 高レベルな記述でも低レベルと同等のパフォーマンス
  3. 恐れなき並行性 — 型システムがデータ競合を防止
  4. 豊富なエコシステム — Cargo と crates.io による優れたパッケージ管理
  5. クロスプラットフォーム — 組み込みから Web まで幅広い対応
  6. 優れたツールチェーン — rustfmt、clippy、rust-analyzer による開発体験

学習の指針

  1. まず所有権と借用を理解する(Rust の根幹)
  2. ResultOption を使ったエラーハンドリングに慣れる
  3. トレイトとジェネリクスで抽象化を学ぶ
  4. イテレータとクロージャで関数型プログラミングの手法を習得
  5. async/await で非同期プログラミングに進む
  6. 実際のプロジェクト(CLI ツール、Web API など)で実践

Rust は学習曲線が急と言われるが、コンパイラのエラーメッセージが非常に親切であり、「コンパイルが通れば正しく動く」という高い信頼性を提供する。初期の学習コストを超えれば、安全で高性能なソフトウェアを効率的に開発できる強力なツールとなる。