blog.dopana

Back

JavaScriptやTypeScriptからRustへ移行する開発者にとって、構文は波括弧や let、アロー関数(クロージャ)など一見馴染み深く感じられます。しかしその内部には、全く異なる実行モデルとメモリ設計が存在しています。

10歳でもわかる説明:賃貸マンション vs 専用の精密工房#

  • JavaScript (フルサービスの賃貸マンション): 入居して電気や水道を自由に使え、ゴミは廊下に置いておけます。定期的に清掃員(ガベージコレクタ)が見回って片付けてくれます。探している物がない時は undefinednull が返されます。
  • Rust (専用の精密工作工房): すべての工具に専用のフックと単一の所有者が割り当てられています。借りるのも移動するのも厳密に記録しなければなりません。清掃員はおらず、工具は作業部屋を出た瞬間(コンパイル時確定のRAII破棄)に自動で片付けられます。
graph TD
    subgraph JavaScript ["JavaScript 実行時モデル"]
        JSEval["動的型 & オブジェクト"] --> JSHeap["V8 ヒープメモリ"]
        JSHeap --> GC["バックグラウンドGC(定期的なスレッド停止)"]
        JSEval --> JSNull["null / undefined(実行時TypeErrorリスク)"]
    end
    subgraph Rust ["Rust コンパイル時モデル"]
        RustEval["静的型付け & 型推論"] --> RustCheck["借用チェッカー & 所有権検証"]
        RustCheck --> Bin["最適化された機械語(Zero-cost, GCなし, RAII破棄)"]
        RustEval --> RustEnum["Option<T> & Result<T, E>(コンパイル時強制)"]
    end

1. 変数と不変性(Mutability)#

JavaScriptでは const は再代入を防ぐのみで、オブジェクト内部のプロパティは自由に変更できます。一方Rustの let はデフォルトで完全不変(ディープイミュータブル)です。

javascript.js
// JS: let は再代入可能、const は参照の固定のみ
let x = 10;
x = 20; // OK

const user = { name: "Alice" };
user.name = "Bob"; // OK! オブジェクト内部は変更可能
javascript
rust.rs
// Rust: デフォルトで不変、変更には 'mut' が必要
let x = 10;
// x = 20; // ❌ エラー: 不変変数への再代入不可

let mut y = 10;
y = 20; // ✅ OK

struct User { name: String }
let user = User { name: String::from("Alice") };
// user.name = String::from("Bob"); // ❌ エラー: user が mut でないため変更不可
rust

2. 関数と暗黙の戻り値(Implicit Returns)#

Rustではブロック末尾にセミコロン ; を付けない最後の式が自動的に戻り値となります。

javascript.js
// JavaScript
function add(a, b) {
    return a + b;
}
const multiply = (a, b) => a * b; // アロー関数の暗黙リターン
javascript
rust.rs
// Rust: 型注釈が必須。末尾に ';' を付けない式が戻り値になる
fn add(a: i32, b: i32) -> i32 {
    a + b // セミコロンなし -> 自動的に return
}

// クロージャ(無名関数)
let multiply = |a: i32, b: i32| a * b;
rust

3. オブジェクト・クラス vs 構造体・トレイト#

JavaScriptは this を伴うプロトタイプ/クラスベースのOOPです。Rustはデータ(struct)と振る舞い(impl)、インターフェース(trait)を明確に分離します。

javascript.js
// JavaScript Class
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }
    area() {
        return this.width * this.height;
    }
}
const rect = new Rectangle(10, 20);
console.log(rect.area());
javascript

4. null / undefined vs Option<T>#

JavaScript開発者が最も遭遇する実行時エラーは TypeError: Cannot read properties of undefined です。Rustは Option<T> により、null の概念をコンパイル段階で完全に排除します。

graph LR
    JSNullCheck["JS: user?.address?.city"] -->|実行時にundefinedに遭遇| Crash["実行時TypeError(チェック漏れ時)"]
    RustOption["Rust: Option&lt;String&gt;"] -->|コンパイラがmatchを強制| SafeHandling["コンパイル時に100%安全を保証"]
javascript.js
// JS: null または undefined を返す可能性
function findUser(id) {
    if (id === 1) return { name: "Alice" };
    return null;
}
const user = findUser(2);
// console.log(user.name); // ❌ 実行時エラー: Cannot read properties of null
javascript

5. エラー処理: try/catch vs Result<T, E>#

JavaScriptは例外のスロー(throw)によってスタックを飛び越えます。Rustはエラーを通常の戻り値 Result<T, E> として扱います。

javascript.js
// JS: try / catch / throw
try {
    const data = JSON.parse(rawText);
    console.log(data);
} catch (err) {
    console.error("JSON解析失敗:", err.message);
}
javascript
rust.rs
// Rust: Result<T, E> と ? 演算子
fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> {
    let num = s.trim().parse::<i32>()?;
    Ok(num * 2)
}

fn main() {
    match parse_number("42") {
        Ok(val) => println!("計算結果: {val}"),
        Err(e) => eprintln!("パース失敗: {e}"),
    }
}
rust

比較クイックリファレンス#

比較項目JavaScript / TypeScriptRust
型システム動的型 (JS) / 構造的型 (TS)静的型付け、公称型 (Nominal)
メモリ管理ガベージコレクタ (V8)所有権 (Ownership) & RAII
欠損値null および undefinedOption<T> (Some または None)
エラー処理try / catch / throwResult<T, E>? 演算子
非同期処理単一スレッドイベントループマルチスレッド非同期ランタイム (Tokio)
パッケージ管理npm / bun (package.json)cargo (Cargo.toml)
不変性const(浅い参照固定のみ)let(デフォルトで完全不変)

まとめ#

  • Rustの構文はJS/TS開発者にとって直感的ですが、コンパイル時の検証ルールが厳格です。
  • 変数はデフォルトで不変であり、変更には明示的な mut が必要です。
  • クラス継承は存在せず、structtrait の組み合わせで設計します。
  • OptionResultnull や例外を置き換え、実行時クラッシュを未然に防ぎます。

参考資料#