跳轉到

2.3 結構與列舉#

自訂型別:struct 與 enum#

Rust 的自訂型別只有兩種骨架:struct(結構) 組合多個資料,enum(列舉) 表達「可能是其中一種」。搭配模式匹配(match),它們能表達大多數的資料模型。

struct:組合資料#

struct User {
    name: String,
    age: u32,
    active: bool,
}

fn main() {
    // 建立實例
    let u = User {
        name: String::from("小美"),
        age: 25,
        active: true,
    };

    // 存取欄位
    println!("{}", u.name);

    // 「欄位初始化簡寫」:變數名與欄位名相同時
    let name = String::from("阿明");
    let age = 30;
    let u2 = User { name, age, active: false };

    // struct 更新語法:以另一個實例為基底
    let u3 = User { active: true, ..u2 };
}

方法(method):impl 區塊#

struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    // 關聯函式(沒有 self):像靜態方法,慣用作為建構子
    fn new(width: f64, height: f64) -> Self {
        Self { width, height }
    }

    // 方法(&self:借用自己,唯讀)
    fn area(&self) -> f64 {
        self.width * self.height
    }

    // 可變方法(&mut self)
    fn scale(&mut self, factor: f64) {
        self.width *= factor;
        self.height *= factor;
    }
}

fn main() {
    let mut r = Rectangle::new(3.0, 4.0);
    println!("{}", r.area());   // 12
    r.scale(2.0);
    println!("{}", r.area());   // 24
}

三種 self 的區別(對應第三章的借用規則):

  • &self:唯讀借用(最常見)
  • &mut self:可變借用(要改欄位時)
  • self:拿走所有權(少用,如轉換成別的型別)

enum:可能是其中一種#

enum Shape {
    Circle(f64),                  // 帶資料的變體
    Rectangle { w: f64, h: f64 }, // 帶命名欄位的變體
    Point,                        // 不帶資料的變體
}

fn area(s: &Shape) -> f64 {
    match s {
        Shape::Circle(r) => 3.14159 * r * r,
        Shape::Rectangle { w, h } => w * h,
        Shape::Point => 0.0,
    }
}

每個變體可以帶不同的資料——這是 Rust enum 比 C 的 enum 強大之處:它表達的是「這個值是這幾種情況之一,每種情況帶各自的資料」。

標準庫最重要的兩個 enum:Option 與 Result#

複習 1.2 節,它們就是 enum:

enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

match:窮盡性檢查#

match 要求覆蓋所有情況——漏掉任何一個變體,編譯錯誤:

fn describe(n: Option<u32>) -> String {
    match n {
        Some(0) => String::from("零"),
        Some(x) if x > 100 => format!("很大:{}", x),  // guard 條件
        Some(x) => format!("有值:{}", x),
        // None => ...   ← 不寫這行,編譯錯誤:漏了 None
    }
}

窮盡性是工程上的珍寶:未來加了一個新變體,所有漏處理的 match 會在編譯期全部被抓出來。

常用簡寫:if let#

只想處理一種情況時,if let 比 match 簡潔:

if let Some(name) = maybe_name {
    println!("有值:{}", name);
}   // None 的情況靜默忽略(要處理就用 match)

本章小結#

  • struct 組合資料,enum 表達「其中一種」,兩者構成 Rust 的資料模型
  • 方法寫在 impl 區塊,三種 self:&self 唯讀、&mut self 可變、self 拿走
  • enum 變體可帶資料;Option 與 Result 就是 enum
  • match 窮盡性檢查:漏情況編譯不過,新增變體時全部受影響處被抓出
  • 只處理一種情況用 if let

想一想#

  1. enum「每個變體帶不同資料」比 C 的 enum 強在哪裡?舉一個你專案中的例子。
  2. 窮盡性檢查對「未來新增功能」有什麼保護作用?
  3. 三種 self 各對應第三章的哪種借用?為什麼 &self 最常見?