iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
Rust

30天Rust從零到全端系列 第 3

Day 3: Rust 基礎語法 - 變數與資料型別

  • 分享至 

  • xImage
  •  

前言

Hi 大家,經過前兩天的環境建置和 Cargo 工具學習,今天我們將要開始深入 Rust 語言的核心——變數和資料型別。這些是所有程式語言的基礎,但 Rust 有其獨特的設計理念,特別是在記憶體安全和效能方面的考量。

變數宣告與可變性

基本變數宣告

在 Rust 中,使用 let 關鍵字來宣告變數:

fn main() {
    let x = 5;
    println!("x 的值是: {}", x);
}

變數的不可變性

Rust 中的變數預設是不可變的(immutable),這是 Rust 安全性設計的重要特色:

fn main() {
    let x = 5;
    println!("x 的值是: {}", x);
    
    // 這行會產生編譯錯誤!
    // x = 6; // 錯誤:cannot assign twice to immutable variable
}

可變變數

如果需要修改變數的值,必須使用 mut 關鍵字:

fn main() {
    let mut x = 5;
    println!("x 的值是: {}", x);
    
    x = 6; // 現在可以修改了
    println!("x 的新值是: {}", x);
}

變數遮蔽(Shadowing)

Rust 允許用同樣的名字宣告新變數,這稱為「遮蔽」:

fn main() {
    let x = 5;
    let x = x + 1; // 遮蔽前一個 x
    let x = x * 2; // 再次遮蔽
    
    println!("x 的值是: {}", x); // 輸出: 12
    
    // 甚至可以改變型別
    let spaces = "   ";
    let spaces = spaces.len(); // 從字串變成數字
    println!("空格數量: {}", spaces);
}

基本資料型別

整數型別

Rust 提供多種整數型別,分為有號和無號:

fn main() {
    // 有號整數(可以是負數)
    let signed_8: i8 = -128;      // -128 到 127
    let signed_16: i16 = -32768;  // -32,768 到 32,767
    let signed_32: i32 = -2000;   // 預設整數型別
    let signed_64: i64 = -4000;   // 64 位元
    let signed_128: i128 = -8000; // 128 位元
    let signed_size: isize = -100; // 依系統架構決定
    
    // 無號整數(只能是正數或零)
    let unsigned_8: u8 = 255;     // 0 到 255
    let unsigned_16: u16 = 65535; // 0 到 65,535
    let unsigned_32: u32 = 4000;  // 0 到 4,294,967,295
    let unsigned_64: u64 = 8000;  // 更大範圍
    let unsigned_128: u128 = 16000; // 最大範圍
    let unsigned_size: usize = 100; // 依系統架構決定
    
    println!("signed_32: {}, unsigned_32: {}", signed_32, unsigned_32);
}

數字字面值的表示方式

fn main() {
    let decimal = 98_222;      // 十進位,可用底線分隔
    let hex = 0xff;            // 十六進位
    let octal = 0o77;          // 八進位
    let binary = 0b1111_0000;  // 二進位
    let byte = b'A';           // 位元組(僅限 u8)
    
    println!("十進位: {}, 十六進位: {}, 八進位: {}", decimal, hex, octal);
    println!("二進位: {}, 位元組: {}", binary, byte);
}

浮點數型別

fn main() {
    let f1 = 2.0;      // f64(預設)
    let f2: f32 = 3.0; // f32(單精度)
    
    println!("f64: {}, f32: {}", f1, f2);
    
    // 數學運算
    let sum = f1 + f2 as f64; // 需要型別轉換
    println!("總和: {}", sum);
}

布林型別

fn main() {
    let is_true = true;
    let is_false: bool = false;
    
    println!("真: {}, 假: {}", is_true, is_false);
    
    // 布林運算
    let result = is_true && !is_false;
    println!("運算結果: {}", result);
}

字元型別

fn main() {
    let c = 'z';
    let z = 'ℤ';
    let heart_eyed_cat = '😻';
    let chinese_char = '中';
    
    println!("字元: {}, {}, {}, {}", c, z, heart_eyed_cat, chinese_char);
    
    // 字元是 4 位元組的 Unicode
    println!("字元大小: {} 位元組", std::mem::size_of::<char>());
}

複合資料型別

元組(Tuple)

元組可以將多個不同型別的值組合成一個複合型別:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    
    // 解構賦值
    let (x, y, z) = tup;
    println!("x: {}, y: {}, z: {}", x, y, z);
    
    // 使用索引存取
    let first = tup.0;
    let second = tup.1;
    let third = tup.2;
    println!("第一個: {}, 第二個: {}, 第三個: {}", first, second, third);
    
    // 空元組(單位型別)
    let unit = ();
    println!("單位型別: {:?}", unit);
}

陣列(Array)

陣列中的所有元素必須是相同型別,且長度固定:

fn main() {
    // 基本陣列宣告
    let a = [1, 2, 3, 4, 5];
    let months = ["一月", "二月", "三月", "四月", "五月", "六月"];
    
    // 指定型別和長度
    let b: [i32; 5] = [1, 2, 3, 4, 5];
    
    // 初始化相同值的陣列
    let c = [3; 5]; // 相當於 [3, 3, 3, 3, 3]
    
    println!("第一個陣列: {:?}", a);
    println!("月份陣列: {:?}", months);
    println!("相同值陣列: {:?}", c);
    
    // 存取陣列元素
    let first = a[0];
    let second = a[1];
    println!("第一個元素: {}, 第二個元素: {}", first, second);
    
    // 陣列長度
    println!("陣列長度: {}", a.len());
}

型別推斷與註釋

fn main() {
    // 型別推斷
    let guess = "42".parse().expect("不是數字!"); // 錯誤:型別不明確
    
    // 正確的方式 - 明確指定型別
    let guess: u32 = "42".parse().expect("不是數字!");
    println!("猜測的數字: {}", guess);
    
    // 或使用 turbofish 語法
    let guess2 = "42".parse::<u32>().expect("不是數字!");
    println!("另一個猜測: {}", guess2);
}

常數與靜態變數

常數(Constants)

const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159265359;

fn main() {
    println!("最高分數: {}", MAX_POINTS);
    println!("圓周率: {}", PI);
}

靜態變數(Static Variables)

static HELLO_WORLD: &str = "Hello, world!";
static mut COUNTER: usize = 0;

fn main() {
    println!("{}", HELLO_WORLD);
    
    // 修改靜態可變變數需要 unsafe 區塊
    unsafe {
        COUNTER += 1;
        println!("計數器: {}", COUNTER);
    }
}

實際練習:資料型別轉換工具

我們來建立一個實用的程式來練習今天學到的概念:

use std::io;

fn main() {
    println!("=== 資料型別轉換工具 ===");
    
    loop {
        println!("\n請選擇轉換類型:");
        println!("1. 攝氏轉華氏");
        println!("2. 華氏轉攝氏");
        println!("3. 公里轉英里");
        println!("4. 英里轉公里");
        println!("5. 離開");
        
        let mut input = String::new();
        io::stdin().read_line(&mut input).expect("讀取失敗");
        
        let choice: u32 = match input.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("請輸入有效數字!");
                continue;
            }
        };
        
        match choice {
            1 => celsius_to_fahrenheit(),
            2 => fahrenheit_to_celsius(),
            3 => km_to_miles(),
            4 => miles_to_km(),
            5 => {
                println!("感謝使用!");
                break;
            }
            _ => println!("無效選項,請重新選擇!"),
        }
    }
}

fn celsius_to_fahrenheit() {
    println!("請輸入攝氏溫度:");
    let celsius = read_f64();
    let fahrenheit = celsius * 9.0 / 5.0 + 32.0;
    println!("{}°C = {:.2}°F", celsius, fahrenheit);
}

fn fahrenheit_to_celsius() {
    println!("請輸入華氏溫度:");
    let fahrenheit = read_f64();
    let celsius = (fahrenheit - 32.0) * 5.0 / 9.0;
    println!("{}°F = {:.2}°C", fahrenheit, celsius);
}

fn km_to_miles() {
    println!("請輸入公里數:");
    let km = read_f64();
    let miles = km * 0.621371;
    println!("{} 公里 = {:.2} 英里", km, miles);
}

fn miles_to_km() {
    println!("請輸入英里數:");
    let miles = read_f64();
    let km = miles * 1.60934;
    println!("{} 英里 = {:.2} 公里", miles, km);
}

fn read_f64() -> f64 {
    loop {
        let mut input = String::new();
        io::stdin().read_line(&mut input).expect("讀取失敗");
        
        match input.trim().parse::<f64>() {
            Ok(num) => return num,
            Err(_) => println!("請輸入有效數字!"),
        }
    }
}

總結

今天我們學習了 Rust 中變數和基本資料型別的核心概念。重點包括:

  • 變數預設不可變,需要 mut 關鍵字才能修改
  • 變數遮蔽允許重複使用變數名,甚至改變型別
  • 豐富的數值型別選擇,適合不同精確度需求
  • 複合型別:元組和陣列的使用方式
  • 型別推斷與明確型別註釋的平衡

明天我們將介紹函式的定義和使用 ~


上一篇
Day 2: Cargo 詳解 - Rust 專案管理的核心
下一篇
Day 4: 函式與程式流程控制
系列文
30天Rust從零到全端15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言