在先前的教學中,我們學會了為結構體 (Struct) 實作「方法」(Methods)。方法是隸屬於結構體實例 (instance) 的函式,透過 . 呼叫 (例如 song.display_song_info()
),並且其第一個參數總是 self
、&self
或 &mut self
。
現在,我們要探討另一種與結構體相關的函式,它不隸屬於某個特定的實例,而是直接隸屬於型別 (type) 本身。這就是「關聯函數」(Associated Functions)。
Associated functions are functions that are attached to a type. > (關聯函數是隸屬於某個型別的函式。)
這句話是核心概念。關聯函數和方法最大的區別在於:
self
,代表一個具體的實例。self
作為第一個參數。.
來呼叫它。取而代之,我們使用雙冒號 ::
語法,將其與型別名稱直接關聯起來,格式為 TypeName::function_name()
。你其實已經見過關聯函數了!例如:
String::from("some text")
:from
就是 String
型別的一個關聯函數。String::new():new
也是 String
型別的一個關聯函數,用來創建一個空的 String
。String::
來存取這些函式,而不是透過某個已存在的 string_variable.
。A constructor is a function that returns a new instance of a type. > (建構子是一個回傳型別新實例的函式。)
在許多物件導向語言中,「建構子」是一個特殊的語法,但在 Rust 中,建構子只是一種遵循慣例的普通關聯函數。它的職責很單純:創建並回傳一個結構體的新實例。
按照 Rust 的慣例,這個函數通常被命名為 new
。
#[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 {...}
}
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_info
、double_secs
)這種設計完全合法,且不影響程式執行,只是有助於維護與閱讀。
self
,常用來建構實例(如 new
)。StructName::function_name()
impl
區塊,讓關聯函數與方法分開管理,程式更模組化。with_default_duration()
、from_year_only()
等多種 Self
工廠方法。這樣的設計有助於封裝建構邏輯、統一初始化流程,讓程式碼更具可讀性與彈性。