404 Not Found

404 Not Found


nginx

Rustの変数とデータ型:`let`から型システムまでを網羅したガイド

Rustでは、変数はデフォルトで不変です。これは制限ではなく、安全性を保証するためのものです。

Rustの変数システムと型システムは、メモリの安全性を重視するRustの基盤となっています。不変性、型推論、スカラー型を理解することは、Rustを習得するための第一歩です。


1. 学習内容


2. レジ係の実話

(1) 課題:Excelの数値の誤りを修正する

リサは小さな食料品店のレジ係です。毎日仕事が終わると、彼女はExcelを使ってその日の売上を集計しています:

Rustを学んだ後、彼女はこう述べた。「Excelのセルがデフォルトで編集不可になっていれば、数式を上書きしてしまう心配もなかったのに。」

(2) Rustの変数モデル

RUST
fn main() {
    let total_sales: f64 = 1280.50;   // Immutable by default -- Today's Sales
    // total_sales = 1500.00;         // ❌ Compilation Error: You cannot modify immutable variables!

    let mut daily_changes = 0.0;      // Use mut to declare a mutable variable
    daily_changes = 150.0;            // ✅ Can be modified
    daily_changes = daily_changes + 200.0;

    const TAX_RATE: f64 = 0.08;      // Compile-time constant, Global Fixed
    println!("Tax Rate: {}, Today's Accounts Receivable: {}", TAX_RATE, total_sales * (1.0 + TAX_RATE));
}

Rustの哲学は、「変更する必要がないなら、変更しない」というものです。デフォルトで不変であることで、コードの理解が容易になり、意図しない変更によるバグを減らすことができます。


3. 変数の宣言

(1) let:デフォルトでは不変

RUST
let x = 5;       // Immutable Variable
// x = 10;       // ❌ Compilation Error: cannot assign twice to immutable variable

(2) let mut: 可変変数

RUST
let mut y = 5;   // Mutable Variable
y = 10;          // ✅ Can be reassigned

(3) const:コンパイル時の定数

RUST
const MAX_POINTS: u32 = 100_000;   // The type must be specified.
const PI: f64 = 3.1415926535;
特徴 let let mut const
可変性 不変 可変 不変
実行時に存在する はい はい いいえ(コンパイル時にインライン化される)
型注釈 任意 任意 必須
グローバルスコープ いいえ いいえ はい
式の評価 実行時 実行時 コンパイル時の定数

4. スカラー型

Rustには、整数、浮動小数点数、ブール値、文字という4つの基本的なスカラー型があります。

100%
graph TB
    A[Scalar Types] --> B[Integer: i8/u8/i16/u16/i32/u32/i64/u64/i128/u128]
    A --> C[Floating-point: f32/f64]
    A --> D[Boolean: bool]
    A --> E[Character: char]

(1) 整数型

長さ 符号付き 符号なし 範囲
8ビット i8 u8 -128 ~ 127 / 0 ~ 255
16ビット i16 u16 -32768 ~ 32767 / 0 ~ 65535
32ビット i32 u32 ±21億 / 0 ~ 42億
64ビット i64 u64 ±9.2 × 10¹⁸ / 0 ~ 1.8 × 10¹⁹
128ビット i128 u128 最大通信距離
アーキテクチャ isize usize システムのビット幅と同じ(64ビットシステム = i64/u64)

デフォルトの整数型は i32 です。これは最高のパフォーマンスを発揮し、ほとんどの用途で十分です。

(2) 浮動小数点型

RUST
let a: f32 = 3.14;    // 32-bit precision floating-point (Single Precision)
let b: f64 = 3.141592653589793;  // 64-bit precision floating-point (Double Precision, Default Type)

デフォルトの浮動小数点型は f64 です。最新の CPU では、f64 と f32 の処理速度はほぼ同じですが、f64 の方が精度が高くなります。

(3) ブール型

RUST
let is_ok: bool = true;
let is_not = false;

ブール値は、条件式 ifwhile などの条件文でよく使用されます。

(4) 文字の種類

RUST
let c: char = 'A';       // ASCII Character
let emoji: char = '🦀';  // Unicode Character(4 Byte)
let han: char = 'Rust';    // Unicode character

Rustにおいて、charは4バイトを占めるUnicodeスカラー値です。これはASCIIではありません!これにより、Rustは国際化をネイティブにサポートしています。

種類 サイズ 値の範囲 代表的な用途
bool 1バイト true / false 条件チェック、フラグ
char 4 バイト Unicode スカラー値 1 文字単位の処理
f32 4バイト ±3.4×10³⁸(約7桁の精度) グラフィックス演算、GPUシェーダー
f64 8 バイト ±1.8×10³⁰⁸(約15桁の精度) 科学計算(デフォルトの浮動小数点)
i32 4 バイト ±21億 汎用整数(デフォルト)
u8 1 バイト 0–255 バイトデータ、RGB 値
usize arch システムのビット幅と同じ 配列のインデックス、コンテナのサイズ

5. 型推論とアノテーション

(1) ▶ サンプル:型推論 (難易度 ⭐)

RUST
// ============================================
// Rust compiler infers types based on assignments
// ============================================

fn main() {
    let x = 42;           // Inferred as i32 (Default integer type)
    let y = 3.14;         // Inferred as f64 (Default floating-point type)
    let z = true;         // Inferred as bool
    let c = 'R';          // Inferred as char

    // Can use :type to view (Example in This Section, Not a standard method)
    println!("The value of x is: {}", x);
    println!("The value of y is: {}", y);
}

出力:

TEXT
x The value is: 42
y The value is: 3.14

コンパイラは文脈に基づいて型を自動的に推論するため、開発者は至る所で型注釈を指定する必要はありません。


(2) ▶ サンプル:明示的な型注釈 (難易度 ⭐)

RUST
// ============================================
// Explicitly Specify the Type -- When a specific size or precision is required
// ============================================

fn main() {
let small: u8 = 255;           // u8 Maximum value
let big: i64 = 9_223_372_036_854_775_807;  // An underscore can be used as a digit separator
let precise: f32 = 3.141592653589793;        // f32 May result in loss of precision
let flag: bool = 5 > 3;                      // The result of a Boolean expression

    println!("small: {}, big: {}, precise: {:.10}, flag: {}", small, big, precise, flag);
}

出力:

TEXT
small: 255, big: 9223372036854775807, precise: 3.1415927410, flag: true

f32 の有効桁数は約 7 桁に過ぎないこと、また 3.1415926535897933.1415927410 に切り捨てられていることに注意してください。


(3) ▶ サンプル:変数のシャドウイング(難易度 ⭐⭐)

RUST
// ============================================
// Shadowing: Overwrite a previous variable with the same name
// ============================================

fn main() {
    let x = 5;              // First x
    let x = x + 1;          // Shadowing: Second x, value becomes 6
    let x = x * 2;          // Shadowing: Third x, value becomes 12

    // Shadowing allows for a change in type!
    let name = "Rust";       // &str Type
    let name = name.len();   // became usize Type!

    println!("x value: {}", x);
    println!("name length: {}", name);
}

出力:

TEXT
x value: 12
name length: 4

シャドウイング ≠ 可変性。 シャドウイングは新しい変数(新しいメモリ領域)を作成するのに対し、mut は同じメモリブロックを変更します。シャドウイングでは同じスコープ内で変数の型を変更できますが、mut ではできません。


(4) ▶ サンプル:総合演習—在庫管理ウィジェット(難易度 ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example: Using Variables, Shadowing and Constants in Inventory Management
// Demo let/mut/const/shadowing Practical Applications
// ============================================

const MAX_STOCK: u32 = 10_000;
const DISCOUNT_THRESHOLD: u32 = 500;

fn main() {
    let item_name = "Wireless Mouse";
    let price: f64 = 49.99;
    let mut stock: u32 = 2500;

    println!("=== Inventory Management System ===");
    println!("Products: {}", item_name);
    println!("Unit Price: {:.2} yuan", price);
    println!("Inventory: {} items", stock);

    stock = stock - 300;
    println!("\n--- Shipped Today 300 items ---");
    println!("Remaining Inventory: {} items", stock);

    let price = price * 0.85;
    println!("\n--- Member Discount 15% off ---");
    println!("Member Price: {:.2} yuan", price);

    let price = format!("{:.2} yuan", price);
    println!("Price Tag: {}", price);

    let total_value = (stock as f64) * 49.99;
    println!("\nTotal Inventory Value: {:.2} yuan", total_value);

    if stock > DISCOUNT_THRESHOLD {
        println!("Ample inventory (> {}), No need to restock", DISCOUNT_THRESHOLD);
    } else {
        let needed = MAX_STOCK - stock;
        println!("Out of Stock! Suggested Reorder {} items", needed);
    }

    println!("Maximum Capacity: {} items", MAX_STOCK);
}

出力:

TEXT
=== Inventory Management System ===
Products: Wireless Mouse
Unit Price: 49.99 yuan
Inventory: 2500 items

--- Shipped Today 300 items ---
Remaining Inventory: 2200 items

--- Member Discount 15% off ---
Member Price: 42.49 yuan
Price Tag: 42.49 yuan

Total Inventory Value: 109978.00 yuan
Ample inventory (> 500), No need to restock
Maximum Capacity: 10000 items

この例では、定数 const、不変変数 let、可変変数 let mut、および変数のシャドウイング(シャドウイングされる型が f64 から String に変わる場合)を組み合わせており、これらの異なる変数宣言方法が実際のシナリオにおいてどのように連携して機能するかを示しています。


(5) ▶ サンプル:整数のオーバーフローと安全な演算(難易度 ⭐⭐)

RUST
// ============================================
// Integer Overflow Behavior: debug mode panic vs release mode wrapping
// ============================================

fn main() {
    let max_u8: u8 = 255;
    println!("u8 Maximum value: {}", max_u8);

    // Safe Computation Method (No panic, Returns overflow result)
    let (result, did_overflow) = max_u8.overflowing_add(1);
    println!("255 + 1 (overflowing_add): {}, Did overflow?: {}", result, did_overflow);

    let checked = max_u8.checked_add(1);
    println!("255 + 1 (checked_add): {:?}", checked);

    let saturating = max_u8.saturating_add(100);
    println!("255 + 100 (saturating_add): {}", saturating);

    let wrapping: u8 = max_u8.wrapping_add(1);
    println!("255 + 1 (wrapping_add): {}", wrapping);

    let neg_i8: i8 = -128;
    let (abs_result, overflow) = neg_i8.overflowing_abs();
    println!("i8 absolute value of minimum: {}, Did overflow?: {}", abs_result, overflow);
}

出力:

TEXT
u8 Maximum value: 255
255 + 1 (overflowing_add): 0, Did overflow?: true
255 + 1 (checked_add): None
255 + 100 (saturating_add): 255
255 + 1 (wrapping_add): 0
i8 absolute value of minimum: -128, Did overflow?: true

Rustでは、整数のオーバーフローはデバッグモードではパニックを引き起こしますが、リリースモードではラップアラウンドとなります。オーバーフローの挙動を明示的に制御するには、checked_*saturating_*、およびwrapping_*メソッドを使用することを推奨します。


(6) ▶ サンプル:変数のシャドウイングと型変換の実践的な応用(難易度:⭐⭐⭐)

RUST
// ============================================
// Practical Examples of Variable Shadowing: String Parsing Chain
// ============================================

fn main() {
    let input = "42.5";
    println!("Original Input: {} (Type: &str)", input);

    let input = input.parse::<f64>();
    println!("Analysis Results: {:?}", input);

    let input = match input {
        Ok(value) => value,
        Err(_) => 0.0,
    };
    println!("After unpacking: {} (Type: f64)", input);

    let input = input as i32;
    println!("Round to an integer: {} (Type: i32, Truncation of Decimals)", input);

    let input = input * 2;
    println!("After doubling: {} (Type: i32)", input);

    let input = format!("Result: {}", input);
    println!("Final Output: {} (Type: String)", input);

    let alice_score = "95";
    let alice_score = alice_score.parse::<u32>().unwrap_or(0);
    let alice_grade = if alice_score >= 90 { "A" } else { "B" };
    println!("Alice: Score={}, Level={}", alice_score, alice_grade);
}

出力:

TEXT
Original Input: 42.5 (Type: &str)
Analysis Results: Ok(42.5)
After unpacking: 42.5 (Type: f64)
Round to an integer: 42 (Type: i32, Truncation of Decimals)
After doubling: 84 (Type: i32)
Final Output: Result: 84 (Type: String)
Alice: Score=95, Level=A

変数のシャドウイングにより、同じスコープ内でデータ型を段階的に変換することが可能になります――&strResult<f64>f64i32String――各段階で同じ変数名を使用することで、冗長性のない明確なコードが得られます。


❓ よくある質問

Q i32とu32の違いは何ですか?
A 「i」で始まる値は符号付き(正または負)であり、「u」で始まる値は符号なし(非負のみ)です。
Q Rustのデフォルトがi64ではなくi32なのはなぜですか?
A パフォーマンス上のトレードオフのためです。
Q シャドウイングとlet mutの違いは何ですか?
A シャドウイングは(型が異なる)新しい変数を作成するのに対し、mutは(型を変更せずに)同じ変数を変更します。
Q char は 4 バイトを占めるので、文字列はスペースの無駄ではないですか?
A Rust の文字列は char の配列ではありません。
Q constlet の違いは何ですか?
A const はコンパイル時にインライン展開されますが、let は実行時に割り当てられます。

📖 まとめ


📝 練習問題

  1. 難易度 ⭐:型 u16 の変数を宣言し、値 65535 を代入した後、その値を 65536 に変更しようとして、コンパイラのエラーメッセージを確認してください。
  2. 難易度 ⭐⭐let mut を使用して、1 から 5 までの数字の和を計算する変数を宣言し、その結果を出力するプログラムを作成してください。
  3. 難易度 ⭐⭐⭐const の定数を fn main() の外で宣言し、関数内で使用してみてください。次に、let の変数を関数の外で宣言し、コンパイラエラーを確認してみてください。これにより、const のグローバルスコープを理解するのに役立ちます。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%