iT邦幫忙

0

二、三天學一點點 Rust:來! Structs 與關聯函數、多重impl區塊(33)

  • 分享至 

  • xImage
  •  

📘 Rust 關聯函數與結構函數

在先前的教學中,我們學會了為結構體 (Struct) 實作「方法」(Methods)。方法是隸屬於結構體實例 (instance) 的函式,透過 . 呼叫 (例如 song.display_song_info()),並且其第一個參數總是 self&self&mut self

現在,我們要探討另一種與結構體相關的函式,它不隸屬於某個特定的實例,而是直接隸屬於型別 (type) 本身。這就是「關聯函數」(Associated Functions)。


🧩什麼是關聯函數 (Associated Functions)?

Associated functions are functions that are attached to a type. > (關聯函數是隸屬於某個型別的函式。)

這句話是核心概念。關聯函數和方法最大的區別在於:

  • 方法 (Method):第一個參數是 self,代表一個具體的實例。
  • 關聯函數 (Associated Function):沒有 self 作為第一個參數。
    因為關聯函數不針對某個特定實例,所以我們不能用 . 來呼叫它。取而代之,我們使用雙冒號 :: 語法,將其與型別名稱直接關聯起來,格式為 TypeName::function_name()

你其實已經見過關聯函數了!例如:

  • String::from("some text")from 就是 String 型別的一個關聯函數。
  • String::new():new 也是 String 型別的一個關聯函數,用來創建一個空的 String
    我們透過 String:: 來存取這些函式,而不是透過某個已存在的 string_variable.

🛠️建構子 (Constructors):一種特別的關聯函數

A constructor is a function that returns a new instance of a type. > (建構子是一個回傳型別新實例的函式。)

在許多物件導向語言中,「建構子」是一個特殊的語法,但在 Rust 中,建構子只是一種遵循慣例的普通關聯函數。它的職責很單純:創建並回傳一個結構體的新實例。

按照 Rust 的慣例,這個函數通常被命名為 new


🧱 Struct 定義

#[derive(Debug)]
struct TaylorSwiftSong {
    title: String,
    year: u32,
    duration_secs: u32,
}

這是一個基本的結構,包含三個欄位。


🧩 關聯函數 new()

impl TaylorSwiftSong {
    // 這是一個關聯函數,因為它的第一個參數不是 self
    // 它同時也是一個建構子,因為它回傳一個 Self (即 TaylorSwiftSong) 的新實例
    fn new(title: String, year: u32, duration_secs: u32) -> Self {
        TaylorSwiftSong {
            title,
            year,
            duration_secs,
        }
    } // 關聯函數

    // -- 以下為方法 (methods),因為第一個參數是 &self 或 &mut self --
    //以下不討論,故省略

    // 方法:顯示歌曲資訊
    fn display_song_info(&self) {...}

    // 方法:將歌曲時長加倍 (此範例 main 中未使用)
    fn double_secs(&mut self) {...}

    // 方法:比較兩首歌的長度
    fn is_longer_than(&self, other: &Self) -> bool {...}

    // 方法:計算發行至今的年份
    fn years_since_release(&self) -> u32 {...}
}

🔍關聯函數 new 的規則、概念

  • 定義位置:關聯函數和方法一樣,都定義在 impl 區塊內。
  • self 參數:fn new(title: String, ...) 的參數列表中沒有 self。這是它與方法的根本區別。
  • 回傳 Self:它回傳 Self。在 impl TaylorSwiftSong 區塊中,Self (大寫 S) 是 TaylorSwiftSong 型別的別名。使用 Self 的好處是,如果未來你修改了結構體的名稱,你只需要改 struct 和 impl 後面的名稱,而不需要修改回傳型別,程式碼更具維護性。
  • 職責:它的工作就是接收創建實例所需的所有參數,然後組裝成一個結構體實例並回傳。

🚀 使用關聯函數來建構實例

fn main() {
    // 使用 :: 語法呼叫 new 關聯函數來創建實例
    let blank_space = TaylorSwiftSong::new(String::from("Blank Space"), 2014, 231);
    // 結構函數 (Constructor)

    // 呼叫實例上的方法
    blank_space.display_song_info();

    // 為了比較,我們直接用傳統方式創建另一個實例
    let all_too_well = TaylorSwiftSong {
        title: String::from("All too well"),
        year: 2012,
        duration_secs: 327,
    };

    // 條件式:比較兩首歌的長度並印出結果
    if blank_space.is_longer_than(&all_too_well) {...);
    } else {
        println!(...);
    }
}

🔍使用方法

  • 呼叫語法:我們使用雙冒號 :: 來呼叫關聯函數,例如 TaylorSwiftSong::new(...)。這個語法清楚地表明 new 函式是隸屬於 TaylorSwiftSong 這個型別的,而不是某個已存在的實例。
  • 與直接初始化的比較:
    • 直接初始化:let all_too_well = TaylorSwiftSong { ... };
    • 使用建構子:let blank_space = TaylorSwiftSong::new(...);
  • 使用建構子的優點:
    封裝性:將創建實例的邏輯封裝在 new 函式中。如果未來創建 TaylorSwiftSong 的規則變得更複雜(例如,需要進行參數驗證,或某些欄位有預設值),我們只需要修改 new 函式內部即可,而所有呼叫 new 的地方都不需要改動。
    清晰與慣例:TypeName::new() 是 Rust 社群廣泛接受的慣例,任何人看到這段程式碼都能立刻明白這是在創建一個新的實例。

📚 多重 impl 區塊的使用

impl TaylorSwiftSong {
    fn new(title: String, release_year: u32, duration_secs: u32) -> Self {
        Self {
            title,
            release_year,
            duration_secs,
        }
    }
}

impl TaylorSwiftSong {
    fn display_song_info(&self) {
        println!("Title: {}", self.title);
        println!("Years Since Release: {}", self.years_since_release());
        println!("Duration: {} seconds", self.duration_secs);
    }

    fn double_length(&mut self) {...}

    fn is_longer_than(&self, other: &Self) -> bool {...}

    fn years_since_release(&self) -> u32 {...}
}

在 Rust 中,可以為同一個 struct 拆開寫多個 impl 區塊。這在實務中常用於將不同邏輯或功能群組化,讓程式碼更清晰。例如:

  • 一個 impl 負責關聯函數(如 new()
  • 另一個 impl 負責方法(如 display_song_infodouble_secs

這種設計完全合法,且不影響程式執行,只是有助於維護與閱讀。


✅ 結論

  • 🧩 關聯函數是與型別關聯的函數,不使用 self,常用來建構實例(如 new)。
  • 🧱 結構函數只是關聯函數的一種慣例寫法,用來包裝建立 struct 的邏輯。
  • 📞 呼叫方式使用 StructName::function_name()
  • 📚 可使用多個 impl 區塊,讓關聯函數與方法分開管理,程式更模組化。
  • 💡 多個建構邏輯時,也可定義 with_default_duration()from_year_only() 等多種 Self 工廠方法。

這樣的設計有助於封裝建構邏輯、統一初始化流程,讓程式碼更具可讀性與彈性。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言